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

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