diff mbox series

[v1,2/4] pcdctl/list: Add pcdctl-list command to enumerate 'nvdimm' devices

Message ID 20210708183741.2952-3-james.sushanth.anandraj@intel.com (mailing list archive)
State New
Headers show
Series ndctl: Add pcdctl tool with pcdctl list and reconfigure-region commands | expand

Commit Message

James Anandraj July 8, 2021, 6:37 p.m. UTC
From: James Sushanth Anandraj <james.sushanth.anandraj@intel.com>

Add pcdctl-list command to enumerate 'nvdimm' devices. The command
reads pcd data from the 'nvdimm' devices to display information
related to region reconfiguration such as, Is there a pending
reconfiguration request and the status of last request.

Signed-off-by: James Sushanth Anandraj <james.sushanth.anandraj@intel.com>
---
 Makefile.am          |   2 +-
 configure.ac         |   1 +
 pcdctl/Makefile.am   |  17 ++
 pcdctl/builtin.h     |   8 +
 pcdctl/list.c        | 114 +++++++++++++
 pcdctl/list.h        |  11 ++
 pcdctl/pcd.h         | 160 ++++++++++++++++++
 pcdctl/pcdctl.c      |  87 ++++++++++
 pcdctl/reconfigure.c | 379 +++++++++++++++++++++++++++++++++++++++++++
 pcdctl/reconfigure.h |  12 ++
 util/main.h          |   1 +
 11 files changed, 791 insertions(+), 1 deletion(-)
 create mode 100644 pcdctl/Makefile.am
 create mode 100644 pcdctl/builtin.h
 create mode 100644 pcdctl/list.c
 create mode 100644 pcdctl/list.h
 create mode 100644 pcdctl/pcd.h
 create mode 100644 pcdctl/pcdctl.c
 create mode 100644 pcdctl/reconfigure.c
 create mode 100644 pcdctl/reconfigure.h
diff mbox series

Patch

diff --git a/Makefile.am b/Makefile.am
index e7615d8..6b415e3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@ 
 include Makefile.am.in
 
 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
-SUBDIRS = . daxctl/lib ndctl/lib ndctl daxctl
+SUBDIRS = . daxctl/lib ndctl/lib ndctl daxctl pcdctl
 if ENABLE_DOCS
 SUBDIRS += Documentation/ndctl Documentation/daxctl Documentation/pcdctl
 endif
diff --git a/configure.ac b/configure.ac
index acf1044..d00d99d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -225,6 +225,7 @@  AC_CONFIG_FILES([
         ndctl/lib/Makefile
         ndctl/Makefile
         daxctl/Makefile
+        pcdctl/Makefile
         test/Makefile
         Documentation/ndctl/Makefile
         Documentation/daxctl/Makefile
diff --git a/pcdctl/Makefile.am b/pcdctl/Makefile.am
new file mode 100644
index 0000000..1f26faf
--- /dev/null
+++ b/pcdctl/Makefile.am
@@ -0,0 +1,17 @@ 
+include $(top_srcdir)/Makefile.am.in
+
+bin_PROGRAMS = pcdctl
+
+pcdctl_SOURCES =\
+		pcdctl.c \
+		list.c \
+		reconfigure.c \
+		../util/json.c \
+		builtin.h
+
+pcdctl_LDADD =\
+	../ndctl/lib/libndctl.la \
+	../libutil.a \
+	$(UUID_LIBS) \
+	$(KMOD_LIBS) \
+	$(JSON_LIBS)
diff --git a/pcdctl/builtin.h b/pcdctl/builtin.h
new file mode 100644
index 0000000..b1c4b2f
--- /dev/null
+++ b/pcdctl/builtin.h
@@ -0,0 +1,8 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2015-2021 Intel Corporation. All rights reserved. */
+#ifndef _PCDCTL_BUILTIN_H_
+#define _PCDCTL_BUILTIN_H_
+
+struct ndctl_ctx;
+int cmd_list(int argc, const char **argv, struct ndctl_ctx *ctx);
+#endif /* _PCDCTL_BUILTIN_H_ */
diff --git a/pcdctl/list.c b/pcdctl/list.c
new file mode 100644
index 0000000..d594fa9
--- /dev/null
+++ b/pcdctl/list.c
@@ -0,0 +1,114 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2015-2021 Intel Corporation. All rights reserved.
+#include <util/json.h>
+#include <util/filter.h>
+#include <json-c/json.h>
+#include <ndctl/libndctl.h>
+#include <pcdctl/builtin.h>
+#include <util/parse-options.h>
+#include <reconfigure.h>
+
+static struct list_param {
+	const char *bus;
+	bool verbose;
+} param;
+
+struct json_object *pcdctl_list_dimm_to_json(struct ndctl_dimm *dimm)
+{
+	struct json_object *jdimm = json_object_new_object();
+	const char *id = ndctl_dimm_get_unique_id(dimm);
+	unsigned int handle = ndctl_dimm_get_handle(dimm);
+	unsigned short phys_id = ndctl_dimm_get_phys_id(dimm);
+	struct json_object *jobj;
+
+	if (!jdimm)
+		return NULL;
+
+	jobj = json_object_new_string(ndctl_dimm_get_devname(dimm));
+	if (!jobj)
+		goto err;
+	json_object_object_add(jdimm, "dev", jobj);
+	if (id) {
+		jobj = json_object_new_string(id);
+		if (!jobj)
+			goto err;
+		json_object_object_add(jdimm, "id", jobj);
+	}
+	if (handle < UINT_MAX) {
+		jobj = util_json_object_hex(handle, 0);
+		if (!jobj)
+			goto err;
+		json_object_object_add(jdimm, "handle", jobj);
+	}
+	if (phys_id < USHRT_MAX) {
+		jobj = util_json_object_hex(phys_id, 0);
+		if (!jobj)
+			goto err;
+		json_object_object_add(jdimm, "phys_id", jobj);
+	}
+	if (pcdctl_dimm_reconfigure_region_pending(dimm)) {
+		jobj = json_object_new_boolean(true);
+		if (!jobj)
+			goto err;
+		json_object_object_add(jdimm, "reconfigure_pending", jobj);
+	} else {
+		const char *r_status_str = NULL;
+		const int r_status = pcdctl_dimm_reconfigure_status(dimm);
+
+		r_status_str = pcdctl_dimm_reconfigure_status_string(dimm);
+		if (r_status_str) {
+			jobj = json_object_new_string(r_status_str);
+			if (!jobj)
+				goto err;
+			json_object_object_add(jdimm, "reconfigure_status",
+					       jobj);
+		}
+
+		if (r_status >= 0) {
+			jobj = json_object_new_int(r_status);
+			if (!jobj)
+				goto err;
+			json_object_object_add(jdimm, "reconfigure_err_status",
+					       jobj);
+		}
+	}
+	return jdimm;
+err:
+	json_object_put(jdimm);
+	return NULL;
+}
+
+int cmd_list(int argc, const char **argv, struct ndctl_ctx *ctx)
+{
+	const struct option options[] = {
+		OPT_STRING('b', "bus", &param.bus, "bus-id",
+			   "filter by <bus-id>"),
+		OPT_BOOLEAN('v', "verbose", &param.verbose, "turn on debug"),
+		OPT_END(),
+	};
+	const char *const u[] = { "pcdctl list [<options>]", NULL };
+	struct ndctl_bus *bus = NULL;
+	struct json_object *j_dimms = NULL;
+
+	argc = parse_options(argc, argv, options, u, 0);
+	if (param.verbose)
+		ndctl_set_log_priority(ctx, LOG_DEBUG);
+	j_dimms = json_object_new_array();
+	if (!j_dimms)
+		return -ENOMEM;
+	ndctl_bus_foreach(ctx, bus) {
+		struct ndctl_dimm *dimm = NULL;
+
+		if (!util_bus_filter(bus, param.bus))
+			continue;
+		ndctl_dimm_foreach(bus, dimm) {
+			struct json_object *j_dimm = NULL;
+
+			j_dimm = pcdctl_list_dimm_to_json(dimm);
+			if (j_dimm)
+				json_object_array_add(j_dimms, j_dimm);
+		}
+	}
+	util_display_json_array(stdout, j_dimms, 0);
+	return 0;
+}
diff --git a/pcdctl/list.h b/pcdctl/list.h
new file mode 100644
index 0000000..5a4d4c2
--- /dev/null
+++ b/pcdctl/list.h
@@ -0,0 +1,11 @@ 
+/* SPDX-License-Identifier: GPL-2.0*/
+/* Copyright(c) 2021 Intel Corporation. All rights reserved.*/
+
+#ifndef _LIST_H_
+#define _LIST_H_
+
+#include <ndctl/lib/private.h>
+#include <ndctl/libndctl.h>
+#include <util/json.h>
+struct json_object *pcdctl_list_dimm_to_json(struct ndctl_dimm *dimm);
+#endif /* _LIST_H_ */
diff --git a/pcdctl/pcd.h b/pcdctl/pcd.h
new file mode 100644
index 0000000..f49f7eb
--- /dev/null
+++ b/pcdctl/pcd.h
@@ -0,0 +1,160 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
+#ifndef _PCD_H_
+#define _PCD_H_
+#include <stdint.h>
+#include <ccan/short_types/short_types.h>
+#include <string.h>
+#include <acpi.h>
+/**
+ * The module Platform Configuration Data (PCD) refers to a section of the
+ * PMem module that is used to store metadata. The metadata stored in the PCD
+ * is the architected interface between software and platform firmware to
+ * support PMem provisioning. The format of PCD partition ID #1 used for
+ * provisioning is as follows. This is from Section 3 Figure 31 of Provisioning
+ * specification document.
+ * PCD Partition ID 1 - Configuration management usage (64KB)
+ *
+ *			+---------------------+    +---------------+
+ *	 +------------->│Current Configuration+--->│Extension table│
+ *	 │		+---------------------+    +---------------+
+ *	 │
+ * +-----+-------+      +---------------------+    +---------------+
+ * │Configuration+----->│ Configuration Input +--->│Extension table│
+ * │   Header    │      +---------------------+    +---------------+
+ * +-----+-------+
+ *	 │		+---------------------+    +---------------+
+ *	 +------------->│Configuration Output +--->│Extension table│
+ *			+---------------------+    +---------------+
+ * Glossary
+ * --------
+ * PCD - Platform Configuration Data
+ * Config Header - Configuration header
+ * CCUR - Current Configuration
+ * CIN - Configuration Input
+ * COUT - Configuration Output
+ */
+/**
+ * struct pcd_config_header - configuration header
+ * @header: ACPI header
+ * @ccur_data_size: current configuration data size
+ * @ccur_offset: current configuration start offset
+ * @cin_data_size: configuration input data size
+ * @cin_offset: configuration input start offset
+ * @cout_data_size: configuration output data size
+ * @cout_offset: configuration output start offset
+ * The configuration header structure contains two parts - ACPI header
+ * and the body part contains pointers to the current configuration,
+ * configuration input, and configuration output. The structure and its
+ * fields are described in the configuration header section 3.1 and Table 31
+ * in provisioning specification document.
+ */
+struct pcd_config_header {
+	struct acpi_header header;
+	u32 ccur_data_size;
+	u32 ccur_offset;
+	u32 cin_data_size;
+	u32 cin_offset;
+	u32 cout_data_size;
+	u32 cout_offset;
+} __attribute__((packed));
+/**
+ * struct pcd_ccur - current configuration
+ * @header: ACPI header
+ * @status: configuration status
+ * @r1: reserved
+ * @volatile_size: volatile memory size mapped into SPA
+ * @persistent_size: persistent memory size mapped into SPA
+ * The current configuration structure consists of two parts - ACPI header
+ * and the body fields that are created by the platform firmware and
+ * updated on each PMem module during the memory configuration phase of
+ * the platform firmware. The structure and its fields are described in the
+ * section 3.2 and Table 32 in the provisioning specification document.
+ */
+struct pcd_ccur {
+	struct acpi_header header;
+	u16 status;
+	u8 r1[2];
+	u64 volatile_size;
+	u64 persistent_size;
+} __attribute__((packed));
+/**
+ * struct pcd_cin - configuration input
+ * @header: ACPI header
+ * @sequence: sequence number
+ * @r1: reserved
+ * The configuration input structure consists of two parts - ACPI header and
+ * the body fields that represents a configuration request created by the
+ * software. The platform firmware processes this table on the next system
+ * reboot. The structure and its fields are described in the section 3.3 and
+ * Table 33 in the provisioning specification document.
+ */
+struct pcd_cin {
+	struct acpi_header header;
+	u32 sequence;
+	u8 r1[8];
+} __attribute__((packed));
+/**
+ * struct pcd_cout - configuration output
+ * @header: ACPI header
+ * @sequence: sequence number
+ * @status: validation status
+ * @r1: reserved
+ * The configuration output structure consists of two parts - ACPI header and
+ * the body fields that are created by the platform firmware in response to the
+ * software request input configuration input table. The structure and its
+ * fields are described in the section 3.4 and Table 34 in the provisioning
+ * specification document.
+ */
+struct pcd_cout {
+	struct acpi_header header;
+	u32 sequence;
+	u8 status;
+	u8 r1[7];
+} __attribute__((packed));
+/**
+ * struct pcd_get_pcd_input - get pcd input
+ * @partition_id: partition id
+ * @payload_type: payload type
+ * @retreive option: retreive option
+ * @reserved: reserved
+ * @offset: offset
+ * The structure represents the input parameters to get
+ * pcd vendor specific command. The structure and its fields are described in
+ * section 4.1 and Table 41 in the provisioning specification document.
+ */
+struct pcd_get_pcd_input {
+	u8 partition_id;
+	struct {
+		u8 payload_type : 1;
+		u8 retrieve_option : 1;
+		u8 reserved : 6;
+	} __attribute__((packed)) options;
+	u32 offset;
+} __attribute__((packed));
+/**
+ * Human readable pcd status string
+ */
+static const char *const pcd_status_str[] = {
+	"undefined",
+	"success",
+	"reserved",
+	"configuration input error",
+};
+
+/**
+ * PCD small payload size .The value is mentioned in section 4.1 of
+ * provisioning document.
+ */
+#define PCD_SP_SIZE 128u
+/**
+ * PCD dimm partition size.The value is mentioned in section 3 figure 31 of
+ * provisioning document.
+ */
+#define MAX_PCD_SIZE 0x10000u
+/**
+ * The opcode value for get pcd vendor specific command. The value is mentioned
+ * in section 4.1 Table 42 of provisioning document.
+ */
+#define PCD_OPCODE_GET_PCD ((u16)0x0601)
+#endif /* _PCD_H_ */
diff --git a/pcdctl/pcdctl.c b/pcdctl/pcdctl.c
new file mode 100644
index 0000000..17607ac
--- /dev/null
+++ b/pcdctl/pcdctl.c
@@ -0,0 +1,87 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2015-2021 Intel Corporation. All rights reserved.
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util/parse-options.h>
+#include <util/main.h>
+#include <ccan/array_size/array_size.h>
+#include <pcdctl/builtin.h>
+#include <ndctl/libndctl.h>
+
+static const char pcdctl_usage_string[] =
+	"pcdctl [--version] [--help] COMMAND [ARGS]";
+static const char pcdctl_more_info_string[] =
+	"See 'pcdctl help COMMAND' for more information on a specific command."
+	"\n pcdctl --list-cmds to see all available commands";
+
+static int cmd_version(int argc, const char **argv, struct ndctl_ctx *ctx)
+{
+	printf("%s\n", VERSION);
+	return 0;
+}
+
+static int cmd_help(int argc, const char **argv, struct ndctl_ctx *ctx)
+{
+	const char *const builtin_help_subcommands[] = {
+		"list",
+		NULL,
+	};
+	struct option builtin_help_options[] = {
+		OPT_END(),
+	};
+	static const char *builtin_help_usage[] = { "pcdctl help [command]",
+						    NULL };
+
+	argc = parse_options_subcommand(argc, argv, builtin_help_options,
+					builtin_help_subcommands,
+					builtin_help_usage, 0);
+
+	if (!argv[0]) {
+		printf("\n usage: %s\n\n", pcdctl_usage_string);
+		printf("\n %s\n\n", pcdctl_more_info_string);
+		return 0;
+	}
+
+	return help_show_man_page(argv[0], "pcdctl", "PCDCTL_MAN_VIEWER");
+}
+
+static struct cmd_struct commands[] = {
+	{ "version", { cmd_version } },
+	{ "list", { cmd_list } },
+	{ "help", { cmd_help } },
+};
+
+int main(int argc, const char **argv)
+{
+	struct ndctl_ctx *ctx;
+	int rc;
+
+	/* Look for flags.. */
+	argv++;
+	argc--;
+	main_handle_options(&argv, &argc, pcdctl_usage_string, commands,
+			    ARRAY_SIZE(commands));
+
+	if (argc > 0) {
+		if (!prefixcmp(argv[0], "--"))
+			argv[0] += 2;
+	} else {
+		/* The user didn't specify a command; give them help */
+		printf("\n usage: %s\n\n", pcdctl_usage_string);
+		printf("\n %s\n\n", pcdctl_more_info_string);
+		goto out;
+	}
+
+	rc = ndctl_new(&ctx);
+	if (rc)
+		goto out;
+	main_handle_internal_command(argc, argv, ctx, commands,
+				     ARRAY_SIZE(commands), PROG_PCDCTL);
+	ndctl_unref(ctx);
+	fprintf(stderr, "Unknown command: '%s'\n", argv[0]);
+out:
+	return 1;
+}
diff --git a/pcdctl/reconfigure.c b/pcdctl/reconfigure.c
new file mode 100644
index 0000000..1b405f7
--- /dev/null
+++ b/pcdctl/reconfigure.c
@@ -0,0 +1,379 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+#include <ndctl/libndctl.h>
+#include <pcd.h>
+#include <stdbool.h>
+#include <reconfigure.h>
+#include <stdlib.h>
+#include <ccan/minmax/minmax.h>
+
+/**
+ * The location of the configuration input in the pcd partition
+ * is determined by configuration input start offset field. The
+ * field is described in Section 3.1 and Table 31 in provisioning
+ * document
+ */
+static struct pcd_cin *get_cin(struct pcd_config_header const *c)
+{
+	return (struct pcd_cin *)((u8 *)c + c->cin_offset);
+}
+
+/**
+ * The location of the current configuration in the pcd partition
+ * is determined by current configuration start offset field. The
+ * field is described in Section 3.1 and Table 31 in provisioning
+ * document
+ */
+static struct pcd_ccur *get_ccur(struct pcd_config_header const *c)
+{
+	return (struct pcd_ccur *)((u8 *)c + c->ccur_offset);
+}
+
+/**
+ * The location of the configuration output in the pcd partition
+ * is determined by configuration output start offset field. The
+ * field is described in Section 3.1 and Table 31 in provisioning
+ * document
+ */
+static struct pcd_cout *get_cout(struct pcd_config_header const *c)
+{
+	return (struct pcd_cout *)((u8 *)c + c->cout_offset);
+}
+
+/**
+ * To determine if a reconfiguration request is pending we can look at
+ * configuration input data size and sequence number fields.
+ */
+static bool is_pcd_reconfigure_pending(struct pcd_config_header const *ch)
+{
+	struct pcd_cin *cin = NULL;
+	struct pcd_cout *cout = NULL;
+
+	/**
+	 * There is a pending request if configuration input table is
+	 * present and the sequence number in the configuration input
+	 * table is not same as the sequence number in configuration
+	 * output table
+	 */
+	cin = get_cin(ch);
+	cout = get_cout(ch);
+	if (ch->cin_data_size == 0) {
+		return false;
+	} else if (cin->sequence > 0) {
+		if (ch->cout_data_size == 0)
+			return true;
+		else if (cin->sequence != cout->sequence)
+			return true;
+	}
+	return false;
+}
+
+/**
+ * Map the current configuration status value to human readable strings. The
+ * current configuration status field is described in Section 3.2 and Table 32
+ * in provisioning document
+ */
+static const char *get_ccur_status_string(struct pcd_config_header const *ch)
+{
+	struct pcd_ccur *ccur = NULL;
+
+	ccur = get_ccur(ch);
+	if (ch->ccur_data_size == 0)
+		return NULL;
+	if (ccur->status <= 3)
+		return pcd_status_str[ccur->status];
+	if (ccur->status == 5)
+		return pcd_status_str[2];
+	/* conf status is cin error */
+	if (ccur->status >= 4 && ccur->status < 16)
+		return pcd_status_str[3];
+	/* all other values are reserved */
+	return pcd_status_str[2];
+}
+
+/**
+ * Get configuration status field from current configuration structure. The
+ * field is described in Section 3.2 and Table 32 in provisioning document
+ */
+static int get_pcd_ccur_status(struct pcd_config_header const *ch)
+{
+	struct pcd_ccur *ccur = NULL;
+
+	ccur = get_ccur(ch);
+	if (ch->ccur_data_size == 0)
+		return -ENOTTY;
+	return ccur->status;
+}
+
+/**
+ * Function to execute a vendor specific command where input data
+ * can be sent and output data can be received
+ */
+static int execute_vendor_specific_cmd(struct ndctl_dimm *dimm,
+				       const u32 op_code, void *inp,
+				       const u32 inp_size, void *op,
+				       u32 op_size)
+{
+	struct ndctl_cmd *cmd = NULL;
+	size_t bytes;
+
+	if (!dimm || !inp || inp_size == 0 || (op_size > 0 && !op) ||
+	    op_size > PCD_SP_SIZE) {
+		fprintf(stderr, "%s: dimm: %#x vendor cmd param incorrect\n",
+			__func__, ndctl_dimm_get_handle(dimm));
+		return -ENOTTY;
+	}
+	cmd = ndctl_dimm_cmd_new_vendor_specific(dimm, op_code, inp_size,
+						 op_size);
+	if (!cmd)
+		return -ENOTTY;
+	bytes = ndctl_cmd_vendor_set_input(cmd, inp, inp_size);
+	if (bytes != inp_size)
+		return -ENOTTY;
+	ndctl_cmd_submit(cmd);
+	if (op_size > 0) {
+		size_t rbytes = 0;
+
+		rbytes = ndctl_cmd_vendor_get_output(cmd, op, op_size);
+		if (rbytes < op_size)
+			return -ENOTTY;
+	}
+	ndctl_cmd_unref(cmd);
+	return 0;
+}
+
+static inline u32 max_of_three(u32 a, u32 b, u32 c)
+{
+	return max(a, b) > c ? max(a, b) : c;
+}
+
+/**
+ * The maximum pcd table size that needs to be read for purpose of reconfigure
+ * regions is the entire 64 kb configuration management usage sub partition.
+ * The actual table structures could occupy less space. The function helps
+ * to calculate the size that needs to be read to get all the pcd table
+ * structures. PCD format is explained in section 3 of provisioning document.
+ */
+static u32 get_table_size(struct pcd_config_header const *ch)
+{
+	u32 size = 0;
+
+	/**
+	 * Find which table among ccur, cin and cout is the furthest in
+	 * the sub partition. The offset + data size of the furthest
+	 * table rounded up to pcd small payload size and bounded by the maximum
+	 * pcd size would be the furthest we need to read to get all the pcd
+	 * table structures. The fields used are explained in section 3.1 and
+	 * table 31 of the provisioning document.
+	 */
+	size = max_of_three(ch->ccur_offset + ch->ccur_data_size,
+			    ch->cin_offset + ch->cin_data_size,
+			    ch->cout_offset + ch->cout_data_size);
+	size = size > PCD_SP_SIZE ? size - (size % PCD_SP_SIZE) + PCD_SP_SIZE :
+				    PCD_SP_SIZE;
+	size = min(size, MAX_PCD_SIZE);
+	return size;
+}
+
+/**
+ * Given a ndctl dimm object read the pcd and calculate table size to read
+ * and get all pcd table structures.
+ */
+static u32 read_pcd_size(struct ndctl_dimm *dimm)
+{
+	u32 op_code = 0;
+	char inp[PCD_SP_SIZE];
+	char op[PCD_SP_SIZE];
+	struct pcd_get_pcd_input *in = NULL;
+
+	memset(inp, 0, PCD_SP_SIZE);
+	memset(op, 0, PCD_SP_SIZE);
+	in = (struct pcd_get_pcd_input *)inp;
+	in->partition_id = 1;
+	in->options.payload_type = 1;
+	in->options.retrieve_option = 0;
+	in->offset = 0;
+	op_code = cpu_to_be16(PCD_OPCODE_GET_PCD);
+	if (execute_vendor_specific_cmd(dimm, op_code, inp, PCD_SP_SIZE, op,
+					PCD_SP_SIZE) != 0)
+		return 0;
+	return get_table_size((struct pcd_config_header *)op);
+}
+
+/**
+ * Given ndctl dimm object and a preallocated buffer read the pcd upto
+ * the number of bytes mentioned in size field into the buffer
+ */
+static int read_pcd(struct ndctl_dimm *dimm, struct pcd_config_header **pcd,
+		    u32 size)
+{
+	u32 op_code = 0;
+	char inp[PCD_SP_SIZE];
+	char op[PCD_SP_SIZE];
+	char **buf = (char **)pcd;
+	struct pcd_get_pcd_input *in = NULL;
+
+	memset(inp, 0, PCD_SP_SIZE);
+	memset(op, 0, PCD_SP_SIZE);
+	in = (struct pcd_get_pcd_input *)inp;
+	in->partition_id = 1;
+	in->options.payload_type = 1;
+	in->options.retrieve_option = 0;
+	in->offset = 0;
+	op_code = cpu_to_be16(PCD_OPCODE_GET_PCD);
+	while (size > 0) {
+		if (execute_vendor_specific_cmd(dimm, op_code, inp, PCD_SP_SIZE,
+						op, PCD_SP_SIZE) != 0)
+			return -ENOTTY;
+		memcpy((*buf) + in->offset, op, PCD_SP_SIZE);
+		size = size - PCD_SP_SIZE;
+		in->offset = in->offset + PCD_SP_SIZE;
+	}
+	return 0;
+}
+
+/**
+ * Validate checksum field of configuration header.
+ */
+static inline int validate_config_header(struct pcd_config_header *c)
+{
+	return acpi_checksum(c, c->header.length);
+}
+
+/**
+ * Validate signature and checksum fields of current configuration,
+ * configuration input and configuration output tables when they are present.
+ * These tables and the fields are explained in Section 3 of provisioning
+ * document.
+ */
+static inline int validate_config_data(struct pcd_config_header const *ch)
+{
+	int ret = 0;
+
+	if (ch->ccur_data_size > 0) {
+		const char *ccur_sig = "CCUR";
+		struct pcd_ccur *ccur = get_ccur(ch);
+
+		if (memcmp(ccur_sig, &ccur->header.signature, 4) != 0)
+			ret = -ENOTTY;
+		if (acpi_checksum(ccur, ccur->header.length) != 0)
+			ret = -ENOTTY;
+	}
+	if (ch->cin_data_size > 0) {
+		const char *cin_sig = "CIN_";
+		struct pcd_cin *cin = get_cin(ch);
+
+		if (memcmp(cin_sig, &cin->header.signature, 4) != 0)
+			ret = -ENOTTY;
+		if (acpi_checksum(cin, cin->header.length) != 0)
+			ret = -ENOTTY;
+	}
+	if (ch->cout_data_size > 0) {
+		const char *cout_sig = "COUT";
+		struct pcd_cout *cout = get_cout(ch);
+
+		if (memcmp(cout_sig, &cout->header.signature, 4) != 0)
+			ret = -ENOTTY;
+		if (acpi_checksum(cout, cout->header.length) != 0)
+			ret = -ENOTTY;
+	}
+	return ret;
+}
+
+/**
+ * Given a pcd buffer validate checksum and signature of sub tables. The pcd
+ * tables and subtables are explained in section 3 of provisioning document.
+ */
+static int validate_pcd(struct pcd_config_header *pcd)
+{
+	int ret = -ENOTTY;
+
+	if (!pcd)
+		return ret;
+	if (validate_config_header(pcd) != 0)
+		return ret;
+	if (validate_config_data(pcd) != 0)
+		return ret;
+	ret = 0;
+	return ret;
+}
+
+/**
+ * Read pcd data from dimm and if valid pcd is present check if there is
+ * a pending region reconfigure request.
+ */
+bool pcdctl_dimm_reconfigure_region_pending(struct ndctl_dimm *dimm)
+{
+	struct pcd_config_header *buf = NULL;
+	bool ret = false;
+	u32 size = read_pcd_size(dimm);
+
+	if (!size)
+		return ret;
+	buf = (struct pcd_config_header *)calloc(size, sizeof(char));
+	if (!(buf))
+		return ret;
+	if (read_pcd(dimm, &buf, size))
+		goto out;
+	if (validate_pcd(buf))
+		goto out;
+	ret = is_pcd_reconfigure_pending(buf);
+out:
+	free(buf);
+	return ret;
+}
+
+/**
+ * Read pcd data from dimm and if valid pcd and reconfigure request is present.
+ * return human readable configuration status string. This field is explained in
+ * section 3.2 and Table 32 of provisioning specification.
+ */
+const char *pcdctl_dimm_reconfigure_status_string(struct ndctl_dimm *dimm)
+{
+	struct pcd_config_header *buf = NULL;
+	const char *status = NULL;
+	u32 size = read_pcd_size(dimm);
+
+	if (!size)
+		return status;
+	buf = (struct pcd_config_header *)calloc(size, sizeof(char));
+	if (!(buf))
+		return status;
+	if (read_pcd(dimm, &buf, size))
+		goto out;
+	if (validate_pcd(buf))
+		goto out;
+	status = get_ccur_status_string(buf);
+out:
+	free(buf);
+	return status;
+}
+
+/**
+ * Read pcd data from dimm and if valid pcd and reconfiguration request is
+ * present return the configuration status value if it is not success. This
+ * field is explained in section 3.2 and Table 32 of provisioning specification.
+ */
+int pcdctl_dimm_reconfigure_status(struct ndctl_dimm *dimm)
+{
+	struct pcd_config_header *buf = NULL;
+	int status = -1;
+	u32 size = read_pcd_size(dimm);
+
+	if (!size)
+		return status;
+	buf = (struct pcd_config_header *)calloc(size, sizeof(char));
+	if (!(buf))
+		return status;
+	if (read_pcd(dimm, &buf, size))
+		goto out;
+	if (validate_pcd(buf))
+		goto out;
+	status = get_pcd_ccur_status(buf);
+	/* No need to display if status is success */
+	if (status == 1)
+		status = -1;
+out:
+	free(buf);
+	return status;
+}
diff --git a/pcdctl/reconfigure.h b/pcdctl/reconfigure.h
new file mode 100644
index 0000000..b3ba17a
--- /dev/null
+++ b/pcdctl/reconfigure.h
@@ -0,0 +1,12 @@ 
+/* SPDX-License-Identifier: GPL-2.0*/
+/* Copyright(c) 2021 Intel Corporation. All rights reserved.*/
+
+#ifndef _RECONFIGURE_H_
+#define _RECONFIGURE_H_
+
+#include <ndctl/lib/private.h>
+#include <ndctl/libndctl.h>
+bool pcdctl_dimm_reconfigure_region_pending(struct ndctl_dimm *dimm);
+const char *pcdctl_dimm_reconfigure_status_string(struct ndctl_dimm *dimm);
+int pcdctl_dimm_reconfigure_status(struct ndctl_dimm *dimm);
+#endif /* _RECONFIGURE_H_ */
diff --git a/util/main.h b/util/main.h
index c89a843..59a9683 100644
--- a/util/main.h
+++ b/util/main.h
@@ -10,6 +10,7 @@ 
 enum program {
 	PROG_NDCTL,
 	PROG_DAXCTL,
+	PROG_PCDCTL
 };
 
 struct ndctl_ctx;