Blame SOURCES/0032-cxl-add-a-cxl-utility-and-libcxl-library.patch

e0018b
From 6cd8155dd5a600f1bb435a49ebddc1fae2f60e73 Mon Sep 17 00:00:00 2001
e0018b
From: Vishal Verma <vishal.l.verma@intel.com>
e0018b
Date: Thu, 7 Oct 2021 02:21:24 -0600
e0018b
Subject: [PATCH 032/217] cxl: add a cxl utility and libcxl library
e0018b
e0018b
CXL - or Compute eXpress Link - is a new interconnect that extends PCIe
e0018b
to support a wide range of devices, including cache coherent memory
e0018b
expanders. As such, these devices can be new sources of 'persistent
e0018b
memory', and the 'ndctl' umbrella of tools and libraries needs to be able
e0018b
to interact with them.
e0018b
e0018b
Add a new utility and library for managing these CXL memory devices. This
e0018b
is an initial bring-up for interacting with CXL devices, and only includes
e0018b
adding the utility and library infrastructure, parsing device information
e0018b
from sysfs for CXL devices, and providing a 'cxl-list' command to
e0018b
display this information in JSON formatted output.
e0018b
e0018b
Cc: Ben Widawsky <ben.widawsky@intel.com>
e0018b
Cc: Dan Williams <dan.j.williams@intel.com>
e0018b
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
e0018b
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
e0018b
---
e0018b
 .clang-format                        |   1 +
e0018b
 .gitignore                           |   4 +-
e0018b
 Documentation/cxl/Makefile.am        |  58 +++++
e0018b
 Documentation/cxl/cxl-list.txt       |  59 +++++
e0018b
 Documentation/cxl/cxl.txt            |  34 +++
e0018b
 Documentation/cxl/human-option.txt   |   8 +
e0018b
 Documentation/cxl/verbose-option.txt |   5 +
e0018b
 Makefile.am                          |   8 +-
e0018b
 Makefile.am.in                       |   4 +
e0018b
 configure.ac                         |   3 +
e0018b
 cxl/Makefile.am                      |  21 ++
e0018b
 cxl/builtin.h                        |   8 +
e0018b
 cxl/cxl.c                            |  96 ++++++++
e0018b
 cxl/lib/Makefile.am                  |  32 +++
e0018b
 cxl/lib/libcxl.c                     | 345 +++++++++++++++++++++++++++
e0018b
 cxl/lib/libcxl.pc.in                 |  11 +
e0018b
 cxl/lib/libcxl.sym                   |  25 ++
e0018b
 cxl/lib/private.h                    |  29 +++
e0018b
 cxl/libcxl.h                         |  55 +++++
e0018b
 cxl/list.c                           | 121 ++++++++++
e0018b
 util/filter.c                        |  20 ++
e0018b
 util/filter.h                        |   2 +
e0018b
 util/json.c                          |  26 ++
e0018b
 util/json.h                          |   3 +
e0018b
 util/main.h                          |   3 +
e0018b
 25 files changed, 977 insertions(+), 4 deletions(-)
e0018b
 create mode 100644 Documentation/cxl/Makefile.am
e0018b
 create mode 100644 Documentation/cxl/cxl-list.txt
e0018b
 create mode 100644 Documentation/cxl/cxl.txt
e0018b
 create mode 100644 Documentation/cxl/human-option.txt
e0018b
 create mode 100644 Documentation/cxl/verbose-option.txt
e0018b
 create mode 100644 cxl/Makefile.am
e0018b
 create mode 100644 cxl/builtin.h
e0018b
 create mode 100644 cxl/cxl.c
e0018b
 create mode 100644 cxl/lib/Makefile.am
e0018b
 create mode 100644 cxl/lib/libcxl.c
e0018b
 create mode 100644 cxl/lib/libcxl.pc.in
e0018b
 create mode 100644 cxl/lib/libcxl.sym
e0018b
 create mode 100644 cxl/lib/private.h
e0018b
 create mode 100644 cxl/libcxl.h
e0018b
 create mode 100644 cxl/list.c
e0018b
e0018b
diff --git a/.clang-format b/.clang-format
e0018b
index 4e00fff..d2e77d0 100644
e0018b
--- a/.clang-format
e0018b
+++ b/.clang-format
e0018b
@@ -77,6 +77,7 @@ ExperimentalAutoDetectBinPacking: false
e0018b
 # 		-e 's/\(.*foreach.*\)(.*/\1/' \
e0018b
 # 	| sort -u)
e0018b
 ForEachMacros:
e0018b
+  - 'cxl_memdev_foreach'
e0018b
   - 'daxctl_dev_foreach'
e0018b
   - 'daxctl_mapping_foreach'
e0018b
   - 'daxctl_region_foreach'
e0018b
diff --git a/.gitignore b/.gitignore
e0018b
index 53512b2..6a97b92 100644
e0018b
--- a/.gitignore
e0018b
+++ b/.gitignore
e0018b
@@ -16,9 +16,11 @@ Makefile.in
e0018b
 *.1
e0018b
 Documentation/daxctl/asciidoc.conf
e0018b
 Documentation/ndctl/asciidoc.conf
e0018b
-Documentation/ndctl/attrs.adoc
e0018b
+Documentation/cxl/asciidoc.conf
e0018b
 Documentation/daxctl/asciidoctor-extensions.rb
e0018b
 Documentation/ndctl/asciidoctor-extensions.rb
e0018b
+Documentation/cxl/asciidoctor-extensions.rb
e0018b
+Documentation/ndctl/attrs.adoc
e0018b
 .dirstamp
e0018b
 daxctl/config.h
e0018b
 daxctl/daxctl
e0018b
diff --git a/Documentation/cxl/Makefile.am b/Documentation/cxl/Makefile.am
e0018b
new file mode 100644
e0018b
index 0000000..db98dd7
e0018b
--- /dev/null
e0018b
+++ b/Documentation/cxl/Makefile.am
e0018b
@@ -0,0 +1,58 @@
e0018b
+# SPDX-License-Identifier: GPL-2.0
e0018b
+# Copyright (C) 2020-2021 Intel Corporation. All rights reserved.
e0018b
+
e0018b
+if USE_ASCIIDOCTOR
e0018b
+
e0018b
+do_subst = sed -e 's,@Utility@,Cxl,g' -e's,@utility@,cxl,g'
e0018b
+CONFFILE = asciidoctor-extensions.rb
e0018b
+asciidoctor-extensions.rb: ../asciidoctor-extensions.rb.in
e0018b
+	$(AM_V_GEN) $(do_subst) < $< > $@
e0018b
+
e0018b
+else
e0018b
+
e0018b
+do_subst = sed -e 's,UTILITY,cxl,g'
e0018b
+CONFFILE = asciidoc.conf
e0018b
+asciidoc.conf: ../asciidoc.conf.in
e0018b
+	$(AM_V_GEN) $(do_subst) < $< > $@
e0018b
+
e0018b
+endif
e0018b
+
e0018b
+man1_MANS = \
e0018b
+	cxl.1 \
e0018b
+	cxl-list.1
e0018b
+
e0018b
+EXTRA_DIST = $(man1_MANS)
e0018b
+
e0018b
+CLEANFILES = $(man1_MANS)
e0018b
+
e0018b
+XML_DEPS = \
e0018b
+	../../version.m4 \
e0018b
+	../copyright.txt \
e0018b
+	Makefile \
e0018b
+	$(CONFFILE)
e0018b
+
e0018b
+RM ?= rm -f
e0018b
+
e0018b
+if USE_ASCIIDOCTOR
e0018b
+
e0018b
+%.1: %.txt $(XML_DEPS)
e0018b
+	$(AM_V_GEN)$(RM) $@+ $@ && \
e0018b
+		$(ASCIIDOC) -b manpage -d manpage -acompat-mode \
e0018b
+		-I. -rasciidoctor-extensions \
e0018b
+		-amansource=cxl -amanmanual="cxl Manual" \
e0018b
+		-andctl_version=$(VERSION) -o $@+ $< && \
e0018b
+		mv $@+ $@
e0018b
+
e0018b
+else
e0018b
+
e0018b
+%.xml: %.txt $(XML_DEPS)
e0018b
+	$(AM_V_GEN)$(RM) $@+ $@ && \
e0018b
+		$(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
e0018b
+		--unsafe -acxl_version=$(VERSION) -o $@+ $< && \
e0018b
+		mv $@+ $@
e0018b
+
e0018b
+%.1: %.xml $(XML_DEPS)
e0018b
+	$(AM_V_GEN)$(RM) $@ && \
e0018b
+		$(XMLTO) -o . -m ../manpage-normal.xsl man $<
e0018b
+
e0018b
+endif
e0018b
diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
e0018b
new file mode 100644
e0018b
index 0000000..370d5b8
e0018b
--- /dev/null
e0018b
+++ b/Documentation/cxl/cxl-list.txt
e0018b
@@ -0,0 +1,59 @@
e0018b
+// SPDX-License-Identifier: GPL-2.0
e0018b
+
e0018b
+cxl-list(1)
e0018b
+===========
e0018b
+
e0018b
+NAME
e0018b
+----
e0018b
+cxl-list - List CXL capable memory devices, and their attributes in json.
e0018b
+
e0018b
+SYNOPSIS
e0018b
+--------
e0018b
+[verse]
e0018b
+'cxl list' [<options>]
e0018b
+
e0018b
+Walk the CXL capable device hierarchy in the system and list all device
e0018b
+instances along with some of their major attributes.
e0018b
+
e0018b
+EXAMPLE
e0018b
+-------
e0018b
+----
e0018b
+# cxl list --memdevs
e0018b
+{
e0018b
+  "memdev":"mem0",
e0018b
+  "pmem_size":268435456,
e0018b
+  "ram_size":0,
e0018b
+}
e0018b
+----
e0018b
+
e0018b
+OPTIONS
e0018b
+-------
e0018b
+-m::
e0018b
+--memdev=::
e0018b
+	Specify a cxl memory device name to filter the listing. For example:
e0018b
+----
e0018b
+# cxl list --memdev=mem0
e0018b
+{
e0018b
+  "memdev":"mem0",
e0018b
+  "pmem_size":268435456,
e0018b
+  "ram_size":0,
e0018b
+}
e0018b
+----
e0018b
+
e0018b
+-M::
e0018b
+--memdevs::
e0018b
+	Include CXL memory devices in the listing
e0018b
+
e0018b
+-i::
e0018b
+--idle::
e0018b
+	Include idle (not enabled / zero-sized) devices in the listing
e0018b
+
e0018b
+include::human-option.txt[]
e0018b
+
e0018b
+include::verbose-option.txt[]
e0018b
+
e0018b
+include::../copyright.txt[]
e0018b
+
e0018b
+SEE ALSO
e0018b
+--------
e0018b
+linkcxl:ndctl-list[1]
e0018b
diff --git a/Documentation/cxl/cxl.txt b/Documentation/cxl/cxl.txt
e0018b
new file mode 100644
e0018b
index 0000000..41a51c7
e0018b
--- /dev/null
e0018b
+++ b/Documentation/cxl/cxl.txt
e0018b
@@ -0,0 +1,34 @@
e0018b
+// SPDX-License-Identifier: GPL-2.0
e0018b
+
e0018b
+cxl(1)
e0018b
+======
e0018b
+
e0018b
+NAME
e0018b
+----
e0018b
+cxl - Provides enumeration and provisioning commands for CXL platforms
e0018b
+
e0018b
+SYNOPSIS
e0018b
+--------
e0018b
+[verse]
e0018b
+'cxl' [--version] [--help] COMMAND [ARGS]
e0018b
+
e0018b
+OPTIONS
e0018b
+-------
e0018b
+-v::
e0018b
+--version::
e0018b
+  Display the version of the 'cxl' utility.
e0018b
+
e0018b
+-h::
e0018b
+--help::
e0018b
+  Run the 'cxl help' command.
e0018b
+
e0018b
+DESCRIPTION
e0018b
+-----------
e0018b
+The cxl utility provides enumeration and provisioning commands for
e0018b
+the CXL devices managed by the Linux kernel.
e0018b
+
e0018b
+include::../copyright.txt[]
e0018b
+
e0018b
+SEE ALSO
e0018b
+--------
e0018b
+linkcxl:ndctl[1]
e0018b
diff --git a/Documentation/cxl/human-option.txt b/Documentation/cxl/human-option.txt
e0018b
new file mode 100644
e0018b
index 0000000..2f4de7a
e0018b
--- /dev/null
e0018b
+++ b/Documentation/cxl/human-option.txt
e0018b
@@ -0,0 +1,8 @@
e0018b
+// SPDX-License-Identifier: GPL-2.0
e0018b
+
e0018b
+-u::
e0018b
+--human::
e0018b
+	By default the command will output machine-friendly raw-integer
e0018b
+	data. Instead, with this flag, numbers representing storage size
e0018b
+	will be formatted as human readable strings with units, other
e0018b
+	fields are converted to hexadecimal strings.
e0018b
diff --git a/Documentation/cxl/verbose-option.txt b/Documentation/cxl/verbose-option.txt
e0018b
new file mode 100644
e0018b
index 0000000..cb62c8e
e0018b
--- /dev/null
e0018b
+++ b/Documentation/cxl/verbose-option.txt
e0018b
@@ -0,0 +1,5 @@
e0018b
+// SPDX-License-Identifier: GPL-2.0
e0018b
+
e0018b
+-v::
e0018b
+--verbose::
e0018b
+	Emit more debug messages
e0018b
diff --git a/Makefile.am b/Makefile.am
e0018b
index 60a1998..428fd40 100644
e0018b
--- a/Makefile.am
e0018b
+++ b/Makefile.am
e0018b
@@ -1,9 +1,9 @@
e0018b
 include Makefile.am.in
e0018b
 
e0018b
 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
e0018b
-SUBDIRS = . daxctl/lib ndctl/lib ndctl daxctl
e0018b
+SUBDIRS = . cxl/lib daxctl/lib ndctl/lib cxl ndctl daxctl
e0018b
 if ENABLE_DOCS
e0018b
-SUBDIRS += Documentation/ndctl Documentation/daxctl
e0018b
+SUBDIRS += Documentation/ndctl Documentation/daxctl Documentation/cxl
e0018b
 endif
e0018b
 SUBDIRS += test
e0018b
 
e0018b
@@ -87,4 +87,6 @@ libutil_a_SOURCES = \
e0018b
 	util/filter.h \
e0018b
 	util/bitmap.h
e0018b
 
e0018b
-nobase_include_HEADERS = daxctl/libdaxctl.h
e0018b
+nobase_include_HEADERS = \
e0018b
+	daxctl/libdaxctl.h \
e0018b
+	cxl/libcxl.h
e0018b
diff --git a/Makefile.am.in b/Makefile.am.in
e0018b
index bdceda9..aaeee53 100644
e0018b
--- a/Makefile.am.in
e0018b
+++ b/Makefile.am.in
e0018b
@@ -42,3 +42,7 @@ LIBNDCTL_AGE=19
e0018b
 LIBDAXCTL_CURRENT=6
e0018b
 LIBDAXCTL_REVISION=0
e0018b
 LIBDAXCTL_AGE=5
e0018b
+
e0018b
+LIBCXL_CURRENT=1
e0018b
+LIBCXL_REVISION=0
e0018b
+LIBCXL_AGE=0
e0018b
diff --git a/configure.ac b/configure.ac
e0018b
index dc39dbe..dadae0a 100644
e0018b
--- a/configure.ac
e0018b
+++ b/configure.ac
e0018b
@@ -222,12 +222,15 @@ AC_CONFIG_HEADERS(config.h)
e0018b
 AC_CONFIG_FILES([
e0018b
         Makefile
e0018b
         daxctl/lib/Makefile
e0018b
+        cxl/lib/Makefile
e0018b
         ndctl/lib/Makefile
e0018b
         ndctl/Makefile
e0018b
         daxctl/Makefile
e0018b
+        cxl/Makefile
e0018b
         test/Makefile
e0018b
         Documentation/ndctl/Makefile
e0018b
         Documentation/daxctl/Makefile
e0018b
+        Documentation/cxl/Makefile
e0018b
 ])
e0018b
 
e0018b
 AC_OUTPUT
e0018b
diff --git a/cxl/Makefile.am b/cxl/Makefile.am
e0018b
new file mode 100644
e0018b
index 0000000..98606b9
e0018b
--- /dev/null
e0018b
+++ b/cxl/Makefile.am
e0018b
@@ -0,0 +1,21 @@
e0018b
+include $(top_srcdir)/Makefile.am.in
e0018b
+
e0018b
+bin_PROGRAMS = cxl
e0018b
+
e0018b
+DISTCLEANFILES = config.h
e0018b
+BUILT_SOURCES = config.h
e0018b
+config.h: $(srcdir)/Makefile.am
e0018b
+	$(AM_V_GEN) echo "/* Autogenerated by cxl/Makefile.am */" >$@
e0018b
+
e0018b
+cxl_SOURCES =\
e0018b
+		cxl.c \
e0018b
+		list.c \
e0018b
+		../util/json.c \
e0018b
+		builtin.h
e0018b
+
e0018b
+cxl_LDADD =\
e0018b
+	lib/libcxl.la \
e0018b
+	../libutil.a \
e0018b
+	$(UUID_LIBS) \
e0018b
+	$(KMOD_LIBS) \
e0018b
+	$(JSON_LIBS)
e0018b
diff --git a/cxl/builtin.h b/cxl/builtin.h
e0018b
new file mode 100644
e0018b
index 0000000..3797f98
e0018b
--- /dev/null
e0018b
+++ b/cxl/builtin.h
e0018b
@@ -0,0 +1,8 @@
e0018b
+/* SPDX-License-Identifier: GPL-2.0 */
e0018b
+/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */
e0018b
+#ifndef _CXL_BUILTIN_H_
e0018b
+#define _CXL_BUILTIN_H_
e0018b
+
e0018b
+struct cxl_ctx;
e0018b
+int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx);
e0018b
+#endif /* _CXL_BUILTIN_H_ */
e0018b
diff --git a/cxl/cxl.c b/cxl/cxl.c
e0018b
new file mode 100644
e0018b
index 0000000..a7725f8
e0018b
--- /dev/null
e0018b
+++ b/cxl/cxl.c
e0018b
@@ -0,0 +1,96 @@
e0018b
+// SPDX-License-Identifier: GPL-2.0
e0018b
+/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */
e0018b
+/* Copyright (C) 2005 Andreas Ericsson. All rights reserved. */
e0018b
+
e0018b
+/* originally copied from perf and git */
e0018b
+
e0018b
+#include <stdio.h>
e0018b
+#include <errno.h>
e0018b
+#include <string.h>
e0018b
+#include <stdlib.h>
e0018b
+#include <unistd.h>
e0018b
+#include <sys/stat.h>
e0018b
+#include <sys/types.h>
e0018b
+#include <cxl/libcxl.h>
e0018b
+#include <util/parse-options.h>
e0018b
+#include <ccan/array_size/array_size.h>
e0018b
+
e0018b
+#include <util/strbuf.h>
e0018b
+#include <util/util.h>
e0018b
+#include <util/main.h>
e0018b
+#include <cxl/builtin.h>
e0018b
+
e0018b
+const char cxl_usage_string[] = "cxl [--version] [--help] COMMAND [ARGS]";
e0018b
+const char cxl_more_info_string[] =
e0018b
+	"See 'cxl help COMMAND' for more information on a specific command.\n"
e0018b
+	" cxl --list-cmds to see all available commands";
e0018b
+
e0018b
+static int cmd_version(int argc, const char **argv, struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	printf("%s\n", VERSION);
e0018b
+	return 0;
e0018b
+}
e0018b
+
e0018b
+static int cmd_help(int argc, const char **argv, struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	const char * const builtin_help_subcommands[] = {
e0018b
+		"list",
e0018b
+		NULL,
e0018b
+	};
e0018b
+	struct option builtin_help_options[] = {
e0018b
+		OPT_END(),
e0018b
+	};
e0018b
+	const char *builtin_help_usage[] = {
e0018b
+		"cxl help [command]",
e0018b
+		NULL
e0018b
+	};
e0018b
+
e0018b
+	argc = parse_options_subcommand(argc, argv, builtin_help_options,
e0018b
+			builtin_help_subcommands, builtin_help_usage, 0);
e0018b
+
e0018b
+	if (!argv[0]) {
e0018b
+		printf("\n usage: %s\n\n", cxl_usage_string);
e0018b
+		printf("\n %s\n\n", cxl_more_info_string);
e0018b
+		return 0;
e0018b
+	}
e0018b
+
e0018b
+	return help_show_man_page(argv[0], "cxl", "CXL_MAN_VIEWER");
e0018b
+}
e0018b
+
e0018b
+static struct cmd_struct commands[] = {
e0018b
+	{ "version", .c_fn = cmd_version },
e0018b
+	{ "list", .c_fn = cmd_list },
e0018b
+	{ "help", .c_fn = cmd_help },
e0018b
+};
e0018b
+
e0018b
+int main(int argc, const char **argv)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx;
e0018b
+	int rc;
e0018b
+
e0018b
+	/* Look for flags.. */
e0018b
+	argv++;
e0018b
+	argc--;
e0018b
+	main_handle_options(&argv, &argc, cxl_usage_string, commands,
e0018b
+			ARRAY_SIZE(commands));
e0018b
+
e0018b
+	if (argc > 0) {
e0018b
+		if (!prefixcmp(argv[0], "--"))
e0018b
+			argv[0] += 2;
e0018b
+	} else {
e0018b
+		/* The user didn't specify a command; give them help */
e0018b
+		printf("\n usage: %s\n\n", cxl_usage_string);
e0018b
+		printf("\n %s\n\n", cxl_more_info_string);
e0018b
+		goto out;
e0018b
+	}
e0018b
+
e0018b
+	rc = cxl_new(&ctx;;
e0018b
+	if (rc)
e0018b
+		goto out;
e0018b
+	main_handle_internal_command(argc, argv, ctx, commands,
e0018b
+			ARRAY_SIZE(commands), PROG_CXL);
e0018b
+	cxl_unref(ctx);
e0018b
+	fprintf(stderr, "Unknown command: '%s'\n", argv[0]);
e0018b
+out:
e0018b
+	return 1;
e0018b
+}
e0018b
diff --git a/cxl/lib/Makefile.am b/cxl/lib/Makefile.am
e0018b
new file mode 100644
e0018b
index 0000000..277f0cd
e0018b
--- /dev/null
e0018b
+++ b/cxl/lib/Makefile.am
e0018b
@@ -0,0 +1,32 @@
e0018b
+include $(top_srcdir)/Makefile.am.in
e0018b
+
e0018b
+%.pc: %.pc.in Makefile
e0018b
+	$(SED_PROCESS)
e0018b
+
e0018b
+pkginclude_HEADERS = ../libcxl.h
e0018b
+lib_LTLIBRARIES = libcxl.la
e0018b
+
e0018b
+libcxl_la_SOURCES =\
e0018b
+	../libcxl.h \
e0018b
+	private.h \
e0018b
+	../../util/sysfs.c \
e0018b
+	../../util/sysfs.h \
e0018b
+	../../util/log.c \
e0018b
+	../../util/log.h \
e0018b
+	libcxl.c
e0018b
+
e0018b
+libcxl_la_LIBADD =\
e0018b
+	$(UUID_LIBS) \
e0018b
+	$(KMOD_LIBS)
e0018b
+
e0018b
+EXTRA_DIST += libcxl.sym
e0018b
+
e0018b
+libcxl_la_LDFLAGS = $(AM_LDFLAGS) \
e0018b
+	-version-info $(LIBCXL_CURRENT):$(LIBCXL_REVISION):$(LIBCXL_AGE) \
e0018b
+	-Wl,--version-script=$(top_srcdir)/cxl/lib/libcxl.sym
e0018b
+libcxl_la_DEPENDENCIES = libcxl.sym
e0018b
+
e0018b
+pkgconfigdir = $(libdir)/pkgconfig
e0018b
+pkgconfig_DATA = libcxl.pc
e0018b
+EXTRA_DIST += libcxl.pc.in
e0018b
+CLEANFILES += libcxl.pc
e0018b
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
e0018b
new file mode 100644
e0018b
index 0000000..c15e987
e0018b
--- /dev/null
e0018b
+++ b/cxl/lib/libcxl.c
e0018b
@@ -0,0 +1,345 @@
e0018b
+// SPDX-License-Identifier: LGPL-2.1
e0018b
+// Copyright (C) 2020-2021, Intel Corporation. All rights reserved.
e0018b
+#include <stdio.h>
e0018b
+#include <errno.h>
e0018b
+#include <limits.h>
e0018b
+#include <libgen.h>
e0018b
+#include <stdlib.h>
e0018b
+#include <dirent.h>
e0018b
+#include <unistd.h>
e0018b
+#include <sys/stat.h>
e0018b
+#include <sys/types.h>
e0018b
+#include <sys/sysmacros.h>
e0018b
+#include <uuid/uuid.h>
e0018b
+#include <ccan/list/list.h>
e0018b
+#include <ccan/array_size/array_size.h>
e0018b
+
e0018b
+#include <util/log.h>
e0018b
+#include <util/sysfs.h>
e0018b
+#include <util/bitmap.h>
e0018b
+#include <cxl/libcxl.h>
e0018b
+#include "private.h"
e0018b
+
e0018b
+/**
e0018b
+ * struct cxl_ctx - library user context to find "nd" instances
e0018b
+ *
e0018b
+ * Instantiate with cxl_new(), which takes an initial reference.  Free
e0018b
+ * the context by dropping the reference count to zero with
e0018b
+ * cxl_unref(), or take additional references with cxl_ref()
e0018b
+ * @timeout: default library timeout in milliseconds
e0018b
+ */
e0018b
+struct cxl_ctx {
e0018b
+	/* log_ctx must be first member for cxl_set_log_fn compat */
e0018b
+	struct log_ctx ctx;
e0018b
+	int refcount;
e0018b
+	void *userdata;
e0018b
+	int memdevs_init;
e0018b
+	struct list_head memdevs;
e0018b
+	struct kmod_ctx *kmod_ctx;
e0018b
+	void *private_data;
e0018b
+};
e0018b
+
e0018b
+static void free_memdev(struct cxl_memdev *memdev, struct list_head *head)
e0018b
+{
e0018b
+	if (head)
e0018b
+		list_del_from(head, &memdev->list);
e0018b
+	kmod_module_unref(memdev->module);
e0018b
+	free(memdev->firmware_version);
e0018b
+	free(memdev->dev_buf);
e0018b
+	free(memdev->dev_path);
e0018b
+	free(memdev);
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_get_userdata - retrieve stored data pointer from library context
e0018b
+ * @ctx: cxl library context
e0018b
+ *
e0018b
+ * This might be useful to access from callbacks like a custom logging
e0018b
+ * function.
e0018b
+ */
e0018b
+CXL_EXPORT void *cxl_get_userdata(struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	if (ctx == NULL)
e0018b
+		return NULL;
e0018b
+	return ctx->userdata;
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_set_userdata - store custom @userdata in the library context
e0018b
+ * @ctx: cxl library context
e0018b
+ * @userdata: data pointer
e0018b
+ */
e0018b
+CXL_EXPORT void cxl_set_userdata(struct cxl_ctx *ctx, void *userdata)
e0018b
+{
e0018b
+	if (ctx == NULL)
e0018b
+		return;
e0018b
+	ctx->userdata = userdata;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT void cxl_set_private_data(struct cxl_ctx *ctx, void *data)
e0018b
+{
e0018b
+	ctx->private_data = data;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT void *cxl_get_private_data(struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	return ctx->private_data;
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_new - instantiate a new library context
e0018b
+ * @ctx: context to establish
e0018b
+ *
e0018b
+ * Returns zero on success and stores an opaque pointer in ctx.  The
e0018b
+ * context is freed by cxl_unref(), i.e. cxl_new() implies an
e0018b
+ * internal cxl_ref().
e0018b
+ */
e0018b
+CXL_EXPORT int cxl_new(struct cxl_ctx **ctx)
e0018b
+{
e0018b
+	struct kmod_ctx *kmod_ctx;
e0018b
+	struct cxl_ctx *c;
e0018b
+	int rc = 0;
e0018b
+
e0018b
+	c = calloc(1, sizeof(struct cxl_ctx));
e0018b
+	if (!c)
e0018b
+		return -ENOMEM;
e0018b
+
e0018b
+	kmod_ctx = kmod_new(NULL, NULL);
e0018b
+	if (check_kmod(kmod_ctx) != 0) {
e0018b
+		rc = -ENXIO;
e0018b
+		goto out;
e0018b
+	}
e0018b
+
e0018b
+	c->refcount = 1;
e0018b
+	log_init(&c->ctx, "libcxl", "CXL_LOG");
e0018b
+	info(c, "ctx %p created\n", c);
e0018b
+	dbg(c, "log_priority=%d\n", c->ctx.log_priority);
e0018b
+	*ctx = c;
e0018b
+	list_head_init(&c->memdevs);
e0018b
+	c->kmod_ctx = kmod_ctx;
e0018b
+
e0018b
+	return 0;
e0018b
+out:
e0018b
+	free(c);
e0018b
+	return rc;
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_ref - take an additional reference on the context
e0018b
+ * @ctx: context established by cxl_new()
e0018b
+ */
e0018b
+CXL_EXPORT struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	if (ctx == NULL)
e0018b
+		return NULL;
e0018b
+	ctx->refcount++;
e0018b
+	return ctx;
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_unref - drop a context reference count
e0018b
+ * @ctx: context established by cxl_new()
e0018b
+ *
e0018b
+ * Drop a reference and if the resulting reference count is 0 destroy
e0018b
+ * the context.
e0018b
+ */
e0018b
+CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	struct cxl_memdev *memdev, *_d;
e0018b
+
e0018b
+	if (ctx == NULL)
e0018b
+		return;
e0018b
+	ctx->refcount--;
e0018b
+	if (ctx->refcount > 0)
e0018b
+		return;
e0018b
+
e0018b
+	list_for_each_safe(&ctx->memdevs, memdev, _d, list)
e0018b
+		free_memdev(memdev, &ctx->memdevs);
e0018b
+
e0018b
+	kmod_unref(ctx->kmod_ctx);
e0018b
+	info(ctx, "context %p released\n", ctx);
e0018b
+	free(ctx);
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_set_log_fn - override default log routine
e0018b
+ * @ctx: cxl library context
e0018b
+ * @log_fn: function to be called for logging messages
e0018b
+ *
e0018b
+ * The built-in logging writes to stderr. It can be overridden by a
e0018b
+ * custom function, to plug log messages into the user's logging
e0018b
+ * functionality.
e0018b
+ */
e0018b
+CXL_EXPORT void cxl_set_log_fn(struct cxl_ctx *ctx,
e0018b
+		void (*cxl_log_fn)(struct cxl_ctx *ctx, int priority,
e0018b
+			const char *file, int line, const char *fn,
e0018b
+			const char *format, va_list args))
e0018b
+{
e0018b
+	ctx->ctx.log_fn = (log_fn) cxl_log_fn;
e0018b
+	info(ctx, "custom logging function %p registered\n", cxl_log_fn);
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_get_log_priority - retrieve current library loglevel (syslog)
e0018b
+ * @ctx: cxl library context
e0018b
+ */
e0018b
+CXL_EXPORT int cxl_get_log_priority(struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	return ctx->ctx.log_priority;
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_set_log_priority - set log verbosity
e0018b
+ * @priority: from syslog.h, LOG_ERR, LOG_INFO, LOG_DEBUG
e0018b
+ *
e0018b
+ * Note: LOG_DEBUG requires library be built with "configure --enable-debug"
e0018b
+ */
e0018b
+CXL_EXPORT void cxl_set_log_priority(struct cxl_ctx *ctx, int priority)
e0018b
+{
e0018b
+	ctx->ctx.log_priority = priority;
e0018b
+}
e0018b
+
e0018b
+static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base)
e0018b
+{
e0018b
+	const char *devname = devpath_to_devname(cxlmem_base);
e0018b
+	char *path = calloc(1, strlen(cxlmem_base) + 100);
e0018b
+	struct cxl_ctx *ctx = parent;
e0018b
+	struct cxl_memdev *memdev, *memdev_dup;
e0018b
+	char buf[SYSFS_ATTR_SIZE];
e0018b
+	struct stat st;
e0018b
+
e0018b
+	if (!path)
e0018b
+		return NULL;
e0018b
+	dbg(ctx, "%s: base: \'%s\'\n", devname, cxlmem_base);
e0018b
+
e0018b
+	memdev = calloc(1, sizeof(*memdev));
e0018b
+	if (!memdev)
e0018b
+		goto err_dev;
e0018b
+	memdev->id = id;
e0018b
+	memdev->ctx = ctx;
e0018b
+
e0018b
+	sprintf(path, "/dev/cxl/%s", devname);
e0018b
+	if (stat(path, &st) < 0)
e0018b
+		goto err_read;
e0018b
+	memdev->major = major(st.st_rdev);
e0018b
+	memdev->minor = minor(st.st_rdev);
e0018b
+
e0018b
+	sprintf(path, "%s/pmem/size", cxlmem_base);
e0018b
+	if (sysfs_read_attr(ctx, path, buf) < 0)
e0018b
+		goto err_read;
e0018b
+	memdev->pmem_size = strtoull(buf, NULL, 0);
e0018b
+
e0018b
+	sprintf(path, "%s/ram/size", cxlmem_base);
e0018b
+	if (sysfs_read_attr(ctx, path, buf) < 0)
e0018b
+		goto err_read;
e0018b
+	memdev->ram_size = strtoull(buf, NULL, 0);
e0018b
+
e0018b
+	sprintf(path, "%s/payload_max", cxlmem_base);
e0018b
+	if (sysfs_read_attr(ctx, path, buf) < 0)
e0018b
+		goto err_read;
e0018b
+	memdev->payload_max = strtoull(buf, NULL, 0);
e0018b
+	if (memdev->payload_max < 0)
e0018b
+		goto err_read;
e0018b
+
e0018b
+	memdev->dev_path = strdup(cxlmem_base);
e0018b
+	if (!memdev->dev_path)
e0018b
+		goto err_read;
e0018b
+
e0018b
+	sprintf(path, "%s/firmware_version", cxlmem_base);
e0018b
+	if (sysfs_read_attr(ctx, path, buf) < 0)
e0018b
+		goto err_read;
e0018b
+
e0018b
+	memdev->firmware_version = strdup(buf);
e0018b
+	if (!memdev->firmware_version)
e0018b
+		goto err_read;
e0018b
+
e0018b
+	memdev->dev_buf = calloc(1, strlen(cxlmem_base) + 50);
e0018b
+	if (!memdev->dev_buf)
e0018b
+		goto err_read;
e0018b
+	memdev->buf_len = strlen(cxlmem_base) + 50;
e0018b
+
e0018b
+	cxl_memdev_foreach(ctx, memdev_dup)
e0018b
+		if (memdev_dup->id == memdev->id) {
e0018b
+			free_memdev(memdev, NULL);
e0018b
+			free(path);
e0018b
+			return memdev_dup;
e0018b
+		}
e0018b
+
e0018b
+	list_add(&ctx->memdevs, &memdev->list);
e0018b
+	free(path);
e0018b
+	return memdev;
e0018b
+
e0018b
+ err_read:
e0018b
+	free(memdev->firmware_version);
e0018b
+	free(memdev->dev_buf);
e0018b
+	free(memdev->dev_path);
e0018b
+	free(memdev);
e0018b
+ err_dev:
e0018b
+	free(path);
e0018b
+	return NULL;
e0018b
+}
e0018b
+
e0018b
+static void cxl_memdevs_init(struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	if (ctx->memdevs_init)
e0018b
+		return;
e0018b
+
e0018b
+	ctx->memdevs_init = 1;
e0018b
+
e0018b
+	sysfs_device_parse(ctx, "/sys/bus/cxl/devices", "mem", ctx,
e0018b
+			   add_cxl_memdev);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return memdev->ctx;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	cxl_memdevs_init(ctx);
e0018b
+
e0018b
+	return list_top(&ctx->memdevs, struct cxl_memdev, list);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx = memdev->ctx;
e0018b
+
e0018b
+	return list_next(&ctx->memdevs, memdev, list);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_memdev_get_id(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return memdev->id;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT const char *cxl_memdev_get_devname(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return devpath_to_devname(memdev->dev_path);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_memdev_get_major(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return memdev->major;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_memdev_get_minor(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return memdev->minor;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return memdev->pmem_size;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return memdev->ram_size;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	return memdev->firmware_version;
e0018b
+}
e0018b
diff --git a/cxl/lib/libcxl.pc.in b/cxl/lib/libcxl.pc.in
e0018b
new file mode 100644
e0018b
index 0000000..949fcdc
e0018b
--- /dev/null
e0018b
+++ b/cxl/lib/libcxl.pc.in
e0018b
@@ -0,0 +1,11 @@
e0018b
+prefix=@prefix@
e0018b
+exec_prefix=@exec_prefix@
e0018b
+libdir=@libdir@
e0018b
+includedir=@includedir@
e0018b
+
e0018b
+Name: libcxl
e0018b
+Description: Manage CXL devices
e0018b
+Version: @VERSION@
e0018b
+Libs: -L${libdir} -lcxl
e0018b
+Libs.private:
e0018b
+Cflags: -I${includedir}
e0018b
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
e0018b
new file mode 100644
e0018b
index 0000000..2616e5c
e0018b
--- /dev/null
e0018b
+++ b/cxl/lib/libcxl.sym
e0018b
@@ -0,0 +1,25 @@
e0018b
+LIBCXL_1 {
e0018b
+global:
e0018b
+	cxl_get_userdata;
e0018b
+	cxl_set_userdata;
e0018b
+	cxl_get_private_data;
e0018b
+	cxl_set_private_data;
e0018b
+	cxl_ref;
e0018b
+	cxl_get_log_priority;
e0018b
+	cxl_set_log_fn;
e0018b
+	cxl_unref;
e0018b
+	cxl_set_log_priority;
e0018b
+	cxl_new;
e0018b
+	cxl_memdev_get_first;
e0018b
+	cxl_memdev_get_next;
e0018b
+	cxl_memdev_get_id;
e0018b
+	cxl_memdev_get_devname;
e0018b
+	cxl_memdev_get_major;
e0018b
+	cxl_memdev_get_minor;
e0018b
+	cxl_memdev_get_ctx;
e0018b
+	cxl_memdev_get_pmem_size;
e0018b
+	cxl_memdev_get_ram_size;
e0018b
+	cxl_memdev_get_firmware_verison;
e0018b
+local:
e0018b
+        *;
e0018b
+};
e0018b
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
e0018b
new file mode 100644
e0018b
index 0000000..fc88fa1
e0018b
--- /dev/null
e0018b
+++ b/cxl/lib/private.h
e0018b
@@ -0,0 +1,29 @@
e0018b
+/* SPDX-License-Identifier: LGPL-2.1 */
e0018b
+/* Copyright (C) 2020-2021, Intel Corporation. All rights reserved. */
e0018b
+#ifndef _LIBCXL_PRIVATE_H_
e0018b
+#define _LIBCXL_PRIVATE_H_
e0018b
+
e0018b
+#include <libkmod.h>
e0018b
+
e0018b
+#define CXL_EXPORT __attribute__ ((visibility("default")))
e0018b
+
e0018b
+struct cxl_memdev {
e0018b
+	int id, major, minor;
e0018b
+	void *dev_buf;
e0018b
+	size_t buf_len;
e0018b
+	char *dev_path;
e0018b
+	char *firmware_version;
e0018b
+	struct cxl_ctx *ctx;
e0018b
+	struct list_node list;
e0018b
+	unsigned long long pmem_size;
e0018b
+	unsigned long long ram_size;
e0018b
+	int payload_max;
e0018b
+	struct kmod_module *module;
e0018b
+};
e0018b
+
e0018b
+static inline int check_kmod(struct kmod_ctx *kmod_ctx)
e0018b
+{
e0018b
+	return kmod_ctx ? 0 : -ENXIO;
e0018b
+}
e0018b
+
e0018b
+#endif /* _LIBCXL_PRIVATE_H_ */
e0018b
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
e0018b
new file mode 100644
e0018b
index 0000000..fd06790
e0018b
--- /dev/null
e0018b
+++ b/cxl/libcxl.h
e0018b
@@ -0,0 +1,55 @@
e0018b
+/* SPDX-License-Identifier: LGPL-2.1 */
e0018b
+/* Copyright (C) 2020-2021, Intel Corporation. All rights reserved. */
e0018b
+#ifndef _LIBCXL_H_
e0018b
+#define _LIBCXL_H_
e0018b
+
e0018b
+#include <stdarg.h>
e0018b
+#include <unistd.h>
e0018b
+
e0018b
+#ifdef HAVE_UUID
e0018b
+#include <uuid/uuid.h>
e0018b
+#else
e0018b
+typedef unsigned char uuid_t[16];
e0018b
+#endif
e0018b
+
e0018b
+#ifdef __cplusplus
e0018b
+extern "C" {
e0018b
+#endif
e0018b
+
e0018b
+struct cxl_ctx;
e0018b
+struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx);
e0018b
+void cxl_unref(struct cxl_ctx *ctx);
e0018b
+int cxl_new(struct cxl_ctx **ctx);
e0018b
+void cxl_set_log_fn(struct cxl_ctx *ctx,
e0018b
+		void (*log_fn)(struct cxl_ctx *ctx, int priority,
e0018b
+			const char *file, int line, const char *fn,
e0018b
+			const char *format, va_list args));
e0018b
+int cxl_get_log_priority(struct cxl_ctx *ctx);
e0018b
+void cxl_set_log_priority(struct cxl_ctx *ctx, int priority);
e0018b
+void cxl_set_userdata(struct cxl_ctx *ctx, void *userdata);
e0018b
+void *cxl_get_userdata(struct cxl_ctx *ctx);
e0018b
+void cxl_set_private_data(struct cxl_ctx *ctx, void *data);
e0018b
+void *cxl_get_private_data(struct cxl_ctx *ctx);
e0018b
+
e0018b
+struct cxl_memdev;
e0018b
+struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx);
e0018b
+struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev);
e0018b
+int cxl_memdev_get_id(struct cxl_memdev *memdev);
e0018b
+const char *cxl_memdev_get_devname(struct cxl_memdev *memdev);
e0018b
+int cxl_memdev_get_major(struct cxl_memdev *memdev);
e0018b
+int cxl_memdev_get_minor(struct cxl_memdev *memdev);
e0018b
+struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev);
e0018b
+unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev);
e0018b
+unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev);
e0018b
+const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
e0018b
+
e0018b
+#define cxl_memdev_foreach(ctx, memdev) \
e0018b
+        for (memdev = cxl_memdev_get_first(ctx); \
e0018b
+             memdev != NULL; \
e0018b
+             memdev = cxl_memdev_get_next(memdev))
e0018b
+
e0018b
+#ifdef __cplusplus
e0018b
+} /* extern "C" */
e0018b
+#endif
e0018b
+
e0018b
+#endif
e0018b
diff --git a/cxl/list.c b/cxl/list.c
e0018b
new file mode 100644
e0018b
index 0000000..043d20c
e0018b
--- /dev/null
e0018b
+++ b/cxl/list.c
e0018b
@@ -0,0 +1,121 @@
e0018b
+// SPDX-License-Identifier: GPL-2.0
e0018b
+/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */
e0018b
+#include <stdio.h>
e0018b
+#include <errno.h>
e0018b
+#include <stdlib.h>
e0018b
+#include <unistd.h>
e0018b
+#include <limits.h>
e0018b
+#include <util/json.h>
e0018b
+#include <util/filter.h>
e0018b
+#include <json-c/json.h>
e0018b
+#include <cxl/libcxl.h>
e0018b
+#include <util/parse-options.h>
e0018b
+#include <ccan/array_size/array_size.h>
e0018b
+
e0018b
+static struct {
e0018b
+	bool memdevs;
e0018b
+	bool idle;
e0018b
+	bool human;
e0018b
+} list;
e0018b
+
e0018b
+static unsigned long listopts_to_flags(void)
e0018b
+{
e0018b
+	unsigned long flags = 0;
e0018b
+
e0018b
+	if (list.idle)
e0018b
+		flags |= UTIL_JSON_IDLE;
e0018b
+	if (list.human)
e0018b
+		flags |= UTIL_JSON_HUMAN;
e0018b
+	return flags;
e0018b
+}
e0018b
+
e0018b
+static struct {
e0018b
+	const char *memdev;
e0018b
+} param;
e0018b
+
e0018b
+static int did_fail;
e0018b
+
e0018b
+#define fail(fmt, ...) \
e0018b
+do { \
e0018b
+	did_fail = 1; \
e0018b
+	fprintf(stderr, "cxl-%s:%s:%d: " fmt, \
e0018b
+			VERSION, __func__, __LINE__, ##__VA_ARGS__); \
e0018b
+} while (0)
e0018b
+
e0018b
+static int num_list_flags(void)
e0018b
+{
e0018b
+	return list.memdevs;
e0018b
+}
e0018b
+
e0018b
+int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx)
e0018b
+{
e0018b
+	const struct option options[] = {
e0018b
+		OPT_STRING('m', "memdev", &param.memdev, "memory device name",
e0018b
+			   "filter by CXL memory device name"),
e0018b
+		OPT_BOOLEAN('M', "memdevs", &list.memdevs,
e0018b
+			    "include CXL memory device info"),
e0018b
+		OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"),
e0018b
+		OPT_BOOLEAN('u', "human", &list.human,
e0018b
+				"use human friendly number formats "),
e0018b
+		OPT_END(),
e0018b
+	};
e0018b
+	const char * const u[] = {
e0018b
+		"cxl list [<options>]",
e0018b
+		NULL
e0018b
+	};
e0018b
+	struct json_object *jdevs = NULL;
e0018b
+	unsigned long list_flags;
e0018b
+	struct cxl_memdev *memdev;
e0018b
+	int i;
e0018b
+
e0018b
+	argc = parse_options(argc, argv, options, u, 0);
e0018b
+	for (i = 0; i < argc; i++)
e0018b
+		error("unknown parameter \"%s\"\n", argv[i]);
e0018b
+
e0018b
+	if (argc)
e0018b
+		usage_with_options(u, options);
e0018b
+
e0018b
+	if (num_list_flags() == 0) {
e0018b
+		/*
e0018b
+		 * TODO: We likely want to list regions by default if nothing
e0018b
+		 * was explicitly asked for. But until we have region support,
e0018b
+		 * print this error asking for devices explicitly.
e0018b
+		 * Once region support is added, this TODO can be removed.
e0018b
+		 */
e0018b
+		error("please specify entities to list, e.g. using -m/-M\n");
e0018b
+		usage_with_options(u, options);
e0018b
+	}
e0018b
+
e0018b
+	list_flags = listopts_to_flags();
e0018b
+
e0018b
+	cxl_memdev_foreach(ctx, memdev) {
e0018b
+		struct json_object *jdev = NULL;
e0018b
+
e0018b
+		if (!util_cxl_memdev_filter(memdev, param.memdev))
e0018b
+			continue;
e0018b
+
e0018b
+		if (list.memdevs) {
e0018b
+			if (!jdevs) {
e0018b
+				jdevs = json_object_new_array();
e0018b
+				if (!jdevs) {
e0018b
+					fail("\n");
e0018b
+					continue;
e0018b
+				}
e0018b
+			}
e0018b
+
e0018b
+			jdev = util_cxl_memdev_to_json(memdev, list_flags);
e0018b
+			if (!jdev) {
e0018b
+				fail("\n");
e0018b
+				continue;
e0018b
+			}
e0018b
+			json_object_array_add(jdevs, jdev);
e0018b
+		}
e0018b
+	}
e0018b
+
e0018b
+	if (jdevs)
e0018b
+		util_display_json_array(stdout, jdevs, list_flags);
e0018b
+
e0018b
+	if (did_fail)
e0018b
+		return -ENOMEM;
e0018b
+	return 0;
e0018b
+}
e0018b
diff --git a/util/filter.c b/util/filter.c
e0018b
index 8b4aad3..d81dade 100644
e0018b
--- a/util/filter.c
e0018b
+++ b/util/filter.c
e0018b
@@ -12,6 +12,7 @@
e0018b
 #include <util/filter.h>
e0018b
 #include <ndctl/libndctl.h>
e0018b
 #include <daxctl/libdaxctl.h>
e0018b
+#include <cxl/libcxl.h>
e0018b
 
e0018b
 struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *__ident)
e0018b
 {
e0018b
@@ -339,6 +340,25 @@ struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region,
e0018b
 	return NULL;
e0018b
 }
e0018b
 
e0018b
+struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev,
e0018b
+					  const char *ident)
e0018b
+{
e0018b
+	int memdev_id;
e0018b
+
e0018b
+	if (!ident || strcmp(ident, "all") == 0)
e0018b
+		return memdev;
e0018b
+
e0018b
+	if (strcmp(ident, cxl_memdev_get_devname(memdev)) == 0)
e0018b
+		return memdev;
e0018b
+
e0018b
+	if ((sscanf(ident, "%d", &memdev_id) == 1
e0018b
+			|| sscanf(ident, "mem%d", &memdev_id) == 1)
e0018b
+			&& cxl_memdev_get_id(memdev) == memdev_id)
e0018b
+		return memdev;
e0018b
+
e0018b
+	return NULL;
e0018b
+}
e0018b
+
e0018b
 enum ndctl_namespace_mode util_nsmode(const char *mode)
e0018b
 {
e0018b
 	if (!mode)
e0018b
diff --git a/util/filter.h b/util/filter.h
e0018b
index 1e1a41c..9a80d65 100644
e0018b
--- a/util/filter.h
e0018b
+++ b/util/filter.h
e0018b
@@ -29,6 +29,8 @@ struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev,
e0018b
 		const char *ident);
e0018b
 struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region,
e0018b
 		const char *ident);
e0018b
+struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev,
e0018b
+		const char *ident);
e0018b
 
e0018b
 enum ndctl_namespace_mode util_nsmode(const char *mode);
e0018b
 const char *util_nsmode_name(enum ndctl_namespace_mode mode);
e0018b
diff --git a/util/json.c b/util/json.c
e0018b
index a8d2412..3be3a92 100644
e0018b
--- a/util/json.c
e0018b
+++ b/util/json.c
e0018b
@@ -9,6 +9,7 @@
e0018b
 #include <json-c/printbuf.h>
e0018b
 #include <ndctl/libndctl.h>
e0018b
 #include <daxctl/libdaxctl.h>
e0018b
+#include <cxl/libcxl.h>
e0018b
 #include <ccan/array_size/array_size.h>
e0018b
 #include <ccan/short_types/short_types.h>
e0018b
 #include <ndctl.h>
e0018b
@@ -1440,3 +1441,28 @@ struct json_object *util_badblock_rec_to_json(u64 block, u64 count,
e0018b
 	json_object_put(jerr);
e0018b
 	return NULL;
e0018b
 }
e0018b
+
e0018b
+struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
e0018b
+		unsigned long flags)
e0018b
+{
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+	struct json_object *jdev, *jobj;
e0018b
+
e0018b
+	jdev = json_object_new_object();
e0018b
+	if (!devname || !jdev)
e0018b
+		return NULL;
e0018b
+
e0018b
+	jobj = json_object_new_string(devname);
e0018b
+	if (jobj)
e0018b
+		json_object_object_add(jdev, "memdev", jobj);
e0018b
+
e0018b
+	jobj = util_json_object_size(cxl_memdev_get_pmem_size(memdev), flags);
e0018b
+	if (jobj)
e0018b
+		json_object_object_add(jdev, "pmem_size", jobj);
e0018b
+
e0018b
+	jobj = util_json_object_size(cxl_memdev_get_ram_size(memdev), flags);
e0018b
+	if (jobj)
e0018b
+		json_object_object_add(jdev, "ram_size", jobj);
e0018b
+
e0018b
+	return jdev;
e0018b
+}
e0018b
diff --git a/util/json.h b/util/json.h
e0018b
index 0f09e36..91918c8 100644
e0018b
--- a/util/json.h
e0018b
+++ b/util/json.h
e0018b
@@ -55,4 +55,7 @@ struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm);
e0018b
 struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm,
e0018b
 		unsigned long flags);
e0018b
 struct json_object *util_region_capabilities_to_json(struct ndctl_region *region);
e0018b
+struct cxl_memdev;
e0018b
+struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev,
e0018b
+		unsigned long flags);
e0018b
 #endif /* __NDCTL_JSON_H__ */
e0018b
diff --git a/util/main.h b/util/main.h
e0018b
index c89a843..80b55c4 100644
e0018b
--- a/util/main.h
e0018b
+++ b/util/main.h
e0018b
@@ -10,16 +10,19 @@
e0018b
 enum program {
e0018b
 	PROG_NDCTL,
e0018b
 	PROG_DAXCTL,
e0018b
+	PROG_CXL,
e0018b
 };
e0018b
 
e0018b
 struct ndctl_ctx;
e0018b
 struct daxctl_ctx;
e0018b
+struct cxl_ctx;
e0018b
 
e0018b
 struct cmd_struct {
e0018b
 	const char *cmd;
e0018b
 	union {
e0018b
 		int (*n_fn)(int, const char **, struct ndctl_ctx *ctx);
e0018b
 		int (*d_fn)(int, const char **, struct daxctl_ctx *ctx);
e0018b
+		int (*c_fn)(int, const char **, struct cxl_ctx *ctx);
e0018b
 	};
e0018b
 };
e0018b
 
e0018b
-- 
e0018b
2.27.0
e0018b