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

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