@@ -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
@@ -225,6 +225,7 @@ AC_CONFIG_FILES([
ndctl/lib/Makefile
ndctl/Makefile
daxctl/Makefile
+ pcdctl/Makefile
test/Makefile
Documentation/ndctl/Makefile
Documentation/daxctl/Makefile
new file mode 100644
@@ -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)
new file mode 100644
@@ -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_ */
new file mode 100644
@@ -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", ¶m.bus, "bus-id",
+ "filter by <bus-id>"),
+ OPT_BOOLEAN('v', "verbose", ¶m.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;
+}
new file mode 100644
@@ -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_ */
new file mode 100644
@@ -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_ */
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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_ */
@@ -10,6 +10,7 @@
enum program {
PROG_NDCTL,
PROG_DAXCTL,
+ PROG_PCDCTL
};
struct ndctl_ctx;