From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0687C2F80 for ; Thu, 8 Jul 2021 18:37:55 +0000 (UTC) X-IronPort-AV: E=McAfee;i="6200,9189,10039"; a="231332269" X-IronPort-AV: E=Sophos;i="5.84,224,1620716400"; d="scan'208";a="231332269" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Jul 2021 11:37:52 -0700 X-IronPort-AV: E=Sophos;i="5.84,224,1620716400"; d="scan'208";a="411017438" Received: from janandra-mobl.amr.corp.intel.com ([10.251.31.93]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Jul 2021 11:37:51 -0700 From: James Anandraj To: nvdimm@lists.linux.dev, james.sushanth.anandraj@intel.com Subject: [PATCH v1 2/4] pcdctl/list: Add pcdctl-list command to enumerate 'nvdimm' devices Date: Thu, 8 Jul 2021 11:37:39 -0700 Message-Id: <20210708183741.2952-3-james.sushanth.anandraj@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20210708183741.2952-1-james.sushanth.anandraj@intel.com> References: <20210708183741.2952-1-james.sushanth.anandraj@intel.com> Precedence: bulk X-Mailing-List: nvdimm@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: James Sushanth Anandraj 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 --- 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 --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 +#include +#include +#include +#include +#include +#include + +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 "), + OPT_BOOLEAN('v', "verbose", ¶m.verbose, "turn on debug"), + OPT_END(), + }; + const char *const u[] = { "pcdctl list []", 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 +#include +#include +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 +#include +#include +#include +/** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +/** + * 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 +#include +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; -- 2.20.1