All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/4] ndctl: add DIMM firmware update support
@ 2017-12-15 23:23 Dave Jiang
  2017-12-15 23:24 ` [PATCH v3 1/4] ndctl: add support to alloc_intel_cmd for variable payload Dave Jiang
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Dave Jiang @ 2017-12-15 23:23 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

The following series implements support for DIMM firmware update in ndctl.

v3: Addressed Dan's comments
- Removed Intel specific bits from update get info. 
- Added inherited context for related commands
- Moved all input params into new_cmd function.
- Added translated status return

---

Dave Jiang (4):
      ndctl: add support to alloc_intel_cmd for variable payload
      ndctl: add firmware download support functions in libndctl
      ndctl: add firmware update command option for ndctl
      ndctl, test: firmware update unit test


 ndctl/Makefile.am      |    3 
 ndctl/lib/Makefile.am  |    1 
 ndctl/lib/firmware.c   |  119 +++++++++++
 ndctl/lib/intel.c      |  264 +++++++++++++++++++++++
 ndctl/lib/intel.h      |   77 +++++++
 ndctl/lib/libndctl.sym |   15 +
 ndctl/lib/private.h    |   16 +
 ndctl/libndctl.h       |   36 +++
 ndctl/ndctl.c          |    1 
 ndctl/update.c         |  544 ++++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 1074 insertions(+), 2 deletions(-)
 create mode 100644 ndctl/lib/firmware.c
 create mode 100644 ndctl/update.c

--
Signature
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 1/4] ndctl: add support to alloc_intel_cmd for variable payload
  2017-12-15 23:23 [PATCH v3 0/4] ndctl: add DIMM firmware update support Dave Jiang
@ 2017-12-15 23:24 ` Dave Jiang
  2017-12-15 23:24 ` [PATCH v3 2/4] ndctl: add firmware download support functions in libndctl Dave Jiang
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Dave Jiang @ 2017-12-15 23:24 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

Certain payloads have variable size. The existing alloc_intel_cmd()
does not take into account of that. Adding additional size for allocation.
We do waste a little bit of the space because of the smart payload.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 ndctl/lib/intel.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ndctl/lib/intel.c b/ndctl/lib/intel.c
index 3e4260f..b85a682 100644
--- a/ndctl/lib/intel.c
+++ b/ndctl/lib/intel.c
@@ -33,7 +33,7 @@ static struct ndctl_cmd *alloc_intel_cmd(struct ndctl_dimm *dimm,
 		return NULL;
 	}
 
-	size = sizeof(*cmd) + sizeof(struct nd_pkg_intel);
+	size = sizeof(*cmd) + sizeof(struct nd_pkg_intel) + in_size + out_size;
 	cmd = calloc(1, size);
 	if (!cmd)
 		return NULL;

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 2/4] ndctl: add firmware download support functions in libndctl
  2017-12-15 23:23 [PATCH v3 0/4] ndctl: add DIMM firmware update support Dave Jiang
  2017-12-15 23:24 ` [PATCH v3 1/4] ndctl: add support to alloc_intel_cmd for variable payload Dave Jiang
@ 2017-12-15 23:24 ` Dave Jiang
  2017-12-15 23:24 ` [PATCH v3 3/4] ndctl: add firmware update command option for ndctl Dave Jiang
  2017-12-15 23:24 ` [PATCH v3 4/4] ndctl, test: firmware update unit test Dave Jiang
  3 siblings, 0 replies; 7+ messages in thread
From: Dave Jiang @ 2017-12-15 23:24 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

Adding the DSM commands from Intel DSM v1.6 to support firmware update
sequence in the libndctl library as additional dimm_ops.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 ndctl/lib/Makefile.am  |    1 
 ndctl/lib/firmware.c   |  119 ++++++++++++++++++++++
 ndctl/lib/intel.c      |  262 ++++++++++++++++++++++++++++++++++++++++++++++++
 ndctl/lib/intel.h      |   77 ++++++++++++++
 ndctl/lib/libndctl.sym |   15 +++
 ndctl/lib/private.h    |   16 +++
 ndctl/libndctl.h       |   36 +++++++
 7 files changed, 526 insertions(+)
 create mode 100644 ndctl/lib/firmware.c

diff --git a/ndctl/lib/Makefile.am b/ndctl/lib/Makefile.am
index 5e10fde..e3a12e7 100644
--- a/ndctl/lib/Makefile.am
+++ b/ndctl/lib/Makefile.am
@@ -21,6 +21,7 @@ libndctl_la_SOURCES =\
 	hpe1.c \
 	msft.c \
 	ars.c \
+	firmware.c \
 	libndctl.c
 
 libndctl_la_LIBADD =\
diff --git a/ndctl/lib/firmware.c b/ndctl/lib/firmware.c
new file mode 100644
index 0000000..4e69847
--- /dev/null
+++ b/ndctl/lib/firmware.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+#include <stdlib.h>
+#include <limits.h>
+#include <util/log.h>
+#include <ndctl/libndctl.h>
+#include "private.h"
+
+/*
+ * Define the wrappers around the ndctl_dimm_ops for firmware update:
+ */
+NDCTL_EXPORT struct ndctl_cmd *
+ndctl_dimm_cmd_new_fw_get_info(struct ndctl_dimm *dimm)
+{
+	struct ndctl_dimm_ops *ops = dimm->ops;
+
+	if (ops && ops->new_fw_get_info)
+		return ops->new_fw_get_info(dimm);
+	else
+		return NULL;
+}
+
+NDCTL_EXPORT struct ndctl_cmd *
+ndctl_dimm_cmd_new_fw_start_update(struct ndctl_dimm *dimm)
+{
+	struct ndctl_dimm_ops *ops = dimm->ops;
+
+	if (ops && ops->new_fw_start_update)
+		return ops->new_fw_start_update(dimm);
+	else
+		return NULL;
+}
+
+NDCTL_EXPORT struct ndctl_cmd *
+ndctl_dimm_cmd_new_fw_send(struct ndctl_cmd *start, unsigned int offset,
+		unsigned int len, void *data)
+{
+	struct ndctl_dimm_ops *ops = start->dimm->ops;
+
+	if (ops && ops->new_fw_send)
+		return ops->new_fw_send(start, offset, len, data);
+	else
+		return NULL;
+}
+
+NDCTL_EXPORT struct ndctl_cmd *
+ndctl_dimm_cmd_new_fw_finish(struct ndctl_cmd *start)
+{
+	struct ndctl_dimm_ops *ops = start->dimm->ops;
+
+	if (ops && ops->new_fw_finish)
+		return ops->new_fw_finish(start);
+	else
+		return NULL;
+}
+
+NDCTL_EXPORT struct ndctl_cmd *
+ndctl_dimm_cmd_new_fw_abort(struct ndctl_cmd *start)
+{
+	struct ndctl_dimm_ops *ops = start->dimm->ops;
+
+	if (ops && ops->new_fw_finish)
+		return ops->new_fw_abort(start);
+	else
+		return NULL;
+}
+
+NDCTL_EXPORT struct ndctl_cmd *
+ndctl_dimm_cmd_new_fw_finish_query(struct ndctl_cmd *start)
+{
+	struct ndctl_dimm_ops *ops = start->dimm->ops;
+
+	if (ops && ops->new_fw_finish_query)
+		return ops->new_fw_finish_query(start);
+	else
+		return NULL;
+}
+
+
+#define firmware_cmd_op(op, rettype, defretvalue) \
+NDCTL_EXPORT rettype ndctl_cmd_##op(struct ndctl_cmd *cmd) \
+{ \
+	if (cmd->dimm) { \
+		struct ndctl_dimm_ops *ops = cmd->dimm->ops; \
+		if (ops && ops->op) \
+			return ops->op(cmd); \
+	} \
+	return defretvalue; \
+}
+
+firmware_cmd_op(fw_info_get_storage_size, unsigned int, 0)
+firmware_cmd_op(fw_info_get_max_send_len, unsigned int, 0)
+firmware_cmd_op(fw_info_get_query_interval, unsigned int, 0)
+firmware_cmd_op(fw_info_get_max_query_time, unsigned int, 0)
+firmware_cmd_op(fw_info_get_run_version, unsigned long long, 0)
+firmware_cmd_op(fw_info_get_updated_version, unsigned long long, 0)
+firmware_cmd_op(fw_start_get_context, unsigned int, 0)
+firmware_cmd_op(fw_fquery_get_fw_rev, unsigned long long, 0)
+
+NDCTL_EXPORT enum ND_FW_STATUS
+ndctl_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd)
+{
+	struct ndctl_dimm_ops *ops = cmd->dimm->ops;
+
+	if (ops && ops->fw_xlat_firmware_status)
+		return ops->fw_xlat_firmware_status(cmd);
+	else
+		return FW_EUNKNOWN;
+}
diff --git a/ndctl/lib/intel.c b/ndctl/lib/intel.c
index b85a682..047b026 100644
--- a/ndctl/lib/intel.c
+++ b/ndctl/lib/intel.c
@@ -288,6 +288,11 @@ static const char *intel_cmd_desc(int fn)
 	static const char *descs[] = {
 		[ND_INTEL_SMART] = "smart",
 		[ND_INTEL_SMART_THRESHOLD] = "smart_thresh",
+		[ND_INTEL_FW_GET_INFO] = "firmware_get_info",
+		[ND_INTEL_FW_START_UPDATE] = "firmware_start_update",
+		[ND_INTEL_FW_SEND_DATA] = "firmware_send_data",
+		[ND_INTEL_FW_FINISH_UPDATE] = "firmware_finish_update",
+		[ND_INTEL_FW_FINISH_STATUS_QUERY] = "firmware_finish_query",
 		[ND_INTEL_SMART_SET_THRESHOLD] = "smart_set_thresh",
 	};
 	const char *desc = descs[fn];
@@ -299,6 +304,248 @@ static const char *intel_cmd_desc(int fn)
 	return desc;
 }
 
+static struct ndctl_cmd *intel_dimm_cmd_new_fw_get_info(struct ndctl_dimm *dimm)
+{
+	struct ndctl_cmd *cmd;
+
+	BUILD_ASSERT(sizeof(struct nd_intel_fw_info) == 44);
+
+	cmd = alloc_intel_cmd(dimm, ND_INTEL_FW_GET_INFO,
+			0, sizeof(cmd->intel->info));
+	if (!cmd)
+		return NULL;
+
+	cmd->firmware_status = &cmd->intel->info.status;
+	return cmd;
+}
+
+static int intel_fw_get_info_valid(struct ndctl_cmd *cmd)
+{
+	struct nd_pkg_intel *pkg = cmd->intel;
+
+	if (cmd->type != ND_CMD_CALL || cmd->status != 0
+			|| pkg->gen.nd_family != NVDIMM_FAMILY_INTEL
+			|| pkg->gen.nd_command != ND_INTEL_FW_GET_INFO)
+		return -EINVAL;
+	return 0;
+}
+
+#define intel_fw_info_get_field32(cmd, field) \
+static unsigned int intel_cmd_fw_info_get_##field( \
+			struct ndctl_cmd *cmd) \
+{ \
+	if (intel_fw_get_info_valid(cmd) < 0) \
+		return UINT_MAX; \
+	return cmd->intel->info.field; \
+}
+
+#define intel_fw_info_get_field64(cmd, field) \
+static unsigned long long intel_cmd_fw_info_get_##field( \
+			struct ndctl_cmd *cmd) \
+{ \
+	if (intel_fw_get_info_valid(cmd) < 0) \
+		return ULLONG_MAX; \
+	return cmd->intel->info.field; \
+}
+
+intel_fw_info_get_field32(cmd, storage_size)
+intel_fw_info_get_field32(cmd, max_send_len)
+intel_fw_info_get_field32(cmd, query_interval)
+intel_fw_info_get_field32(cmd, max_query_time);
+intel_fw_info_get_field64(cmd, run_version);
+intel_fw_info_get_field64(cmd, updated_version);
+
+static struct ndctl_cmd *intel_dimm_cmd_new_fw_start(struct ndctl_dimm *dimm)
+{
+	struct ndctl_cmd *cmd;
+
+	BUILD_ASSERT(sizeof(struct nd_intel_fw_start) == 8);
+
+	cmd = alloc_intel_cmd(dimm, ND_INTEL_FW_START_UPDATE,
+			sizeof(cmd->intel->start) - 4, 4);
+	if (!cmd)
+		return NULL;
+
+	cmd->firmware_status = &cmd->intel->start.status;
+	return cmd;
+}
+
+static int intel_fw_start_valid(struct ndctl_cmd *cmd)
+{
+	struct nd_pkg_intel *pkg = cmd->intel;
+
+	if (cmd->type != ND_CMD_CALL || cmd->status != 0
+			|| pkg->gen.nd_family != NVDIMM_FAMILY_INTEL
+			|| pkg->gen.nd_command != ND_INTEL_FW_START_UPDATE)
+		return -EINVAL;
+	return 0;
+}
+
+static unsigned int intel_cmd_fw_start_get_context(struct ndctl_cmd *cmd)
+{
+	if (intel_fw_start_valid(cmd) < 0)
+		return UINT_MAX;
+	return cmd->intel->start.context;
+}
+
+static struct ndctl_cmd *intel_dimm_cmd_new_fw_send(struct ndctl_cmd *start,
+		unsigned int offset, unsigned int len, void *data)
+{
+	struct ndctl_cmd *cmd;
+
+	BUILD_ASSERT(sizeof(struct nd_intel_fw_send_data) == 12);
+
+	cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_SEND_DATA,
+		sizeof(cmd->intel->send) + len, 4);
+	if (!cmd)
+		return NULL;
+
+	cmd->intel->send.context = start->intel->start.context;
+	cmd->intel->send.offset = offset;
+	cmd->intel->send.length = len;
+	memcpy(cmd->intel->send.data, data, len);
+	/* the last dword is reserved for status */
+	cmd->firmware_status =
+		(unsigned int *)(&cmd->intel->send.data[0] + len);
+	return cmd;
+}
+
+static struct ndctl_cmd *intel_dimm_cmd_new_fw_finish(struct ndctl_cmd *start)
+{
+	struct ndctl_cmd *cmd;
+
+	BUILD_ASSERT(sizeof(struct nd_intel_fw_finish_update) == 12);
+
+	cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_FINISH_UPDATE,
+			offsetof(struct nd_intel_fw_finish_update, status), 4);
+	if (!cmd)
+		return NULL;
+
+	cmd->intel->finish.context = start->intel->start.context;
+	cmd->intel->finish.ctrl_flags = 0;
+	cmd->firmware_status = &cmd->intel->finish.status;
+	return cmd;
+}
+
+static struct ndctl_cmd *intel_dimm_cmd_new_fw_abort(struct ndctl_cmd *start)
+{
+	struct ndctl_cmd *cmd;
+
+	BUILD_ASSERT(sizeof(struct nd_intel_fw_finish_update) == 12);
+
+	cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_FINISH_UPDATE,
+			sizeof(cmd->intel->finish) - 4, 4);
+	if (!cmd)
+		return NULL;
+
+	cmd->intel->finish.context = start->intel->start.context;
+	cmd->intel->finish.ctrl_flags = 1;
+	cmd->firmware_status = &cmd->intel->finish.status;
+	return cmd;
+}
+
+static struct ndctl_cmd *
+intel_dimm_cmd_new_fw_finish_query(struct ndctl_cmd *start)
+{
+	struct ndctl_cmd *cmd;
+
+	BUILD_ASSERT(sizeof(struct nd_intel_fw_finish_query) == 16);
+
+	cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_FINISH_STATUS_QUERY,
+			4, sizeof(cmd->intel->fquery) - 4);
+	if (!cmd)
+		return NULL;
+
+	cmd->intel->fquery.context = start->intel->start.context;
+	cmd->firmware_status = &cmd->intel->fquery.status;
+	return cmd;
+}
+
+static int intel_fw_fquery_valid(struct ndctl_cmd *cmd)
+{
+	struct nd_pkg_intel *pkg = cmd->intel;
+
+	if (cmd->type != ND_CMD_CALL || cmd->status != 0
+			|| pkg->gen.nd_family != NVDIMM_FAMILY_INTEL
+			|| pkg->gen.nd_command != ND_INTEL_FW_FINISH_STATUS_QUERY)
+		return -EINVAL;
+	return 0;
+}
+
+static unsigned long long
+intel_cmd_fw_fquery_get_fw_rev(struct ndctl_cmd *cmd)
+{
+	if (intel_fw_fquery_valid(cmd) < 0)
+		return ULLONG_MAX;
+	return cmd->intel->fquery.updated_fw_rev;
+}
+
+static enum ND_FW_STATUS
+intel_cmd_fw_xlat_extend_firmware_status(struct ndctl_cmd *cmd,
+		unsigned int status)
+{
+	/*
+	 * Note: the cases commented out are identical to the ones that are
+	 * not. They are there for reference.
+	 */
+	switch (status & ND_INTEL_STATUS_EXTEND_MASK) {
+	case ND_INTEL_STATUS_START_BUSY:
+	/* case ND_INTEL_STATUS_SEND_CTXINVAL: */
+	/* case ND_INTEL_STATUS_FIN_CTXINVAL: */
+	/* case ND_INTEL_STATUS_FQ_CTXINVAL: */
+		if (cmd->intel->gen.nd_command == ND_INTEL_FW_START_UPDATE)
+			return FW_EBUSY;
+		else
+			return FW_EINVAL_CTX;
+	case ND_INTEL_STATUS_FIN_DONE:
+	/* case ND_INTEL_STATUS_FQ_BUSY: */
+		if (cmd->intel->gen.nd_command == ND_INTEL_FW_FINISH_UPDATE)
+			return FW_ALREADY_DONE;
+		else
+			return FW_EBUSY;
+	case ND_INTEL_STATUS_FIN_BAD:
+	/* case ND_INTEL_STATUS_FQ_BAD: */
+		return FW_EBADFW;
+	case ND_INTEL_STATUS_FIN_ABORTED:
+	/* case ND_INTEL_STATUS_FQ_ORDER: */
+		if (cmd->intel->gen.nd_command == ND_INTEL_FW_FINISH_UPDATE)
+			return FW_ABORTED;
+		else
+			return FW_ESEQUENCE;
+	}
+
+	return FW_EUNKNOWN;
+}
+
+static enum ND_FW_STATUS
+intel_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd)
+{
+	unsigned int status = *cmd->firmware_status;
+
+	switch (status & ND_INTEL_STATUS_MASK) {
+	case ND_INTEL_STATUS_SUCCESS:
+		return FW_SUCCESS;
+	case ND_INTEL_STATUS_NOTSUPP:
+		return FW_ENOTSUPP;
+	case ND_INTEL_STATUS_NOTEXIST:
+		return FW_ENOTEXIST;
+	case ND_INTEL_STATUS_INVALPARM:
+		return FW_EINVAL;
+	case ND_INTEL_STATUS_HWERR:
+		return FW_EHWERR;
+	case ND_INTEL_STATUS_RETRY:
+		return FW_ERETRY;
+	case ND_INTEL_STATUS_EXTEND:
+		return intel_cmd_fw_xlat_extend_firmware_status(cmd, status);
+	case ND_INTEL_STATUS_NORES:
+		return FW_ENORES;
+	case ND_INTEL_STATUS_NOTREADY:
+		return FW_ENOTREADY;
+	}
+
+	return FW_EUNKNOWN;
+}
+
 struct ndctl_dimm_ops * const intel_dimm_ops = &(struct ndctl_dimm_ops) {
 	.cmd_desc = intel_cmd_desc,
 	.new_smart = intel_dimm_cmd_new_smart,
@@ -331,4 +578,19 @@ struct ndctl_dimm_ops * const intel_dimm_ops = &(struct ndctl_dimm_ops) {
 	.smart_threshold_set_ctrl_temperature
 		= intel_cmd_smart_threshold_set_ctrl_temperature,
 	.smart_threshold_set_spares = intel_cmd_smart_threshold_set_spares,
+	.new_fw_get_info = intel_dimm_cmd_new_fw_get_info,
+	.fw_info_get_storage_size = intel_cmd_fw_info_get_storage_size,
+	.fw_info_get_max_send_len = intel_cmd_fw_info_get_max_send_len,
+	.fw_info_get_query_interval = intel_cmd_fw_info_get_query_interval,
+	.fw_info_get_max_query_time = intel_cmd_fw_info_get_max_query_time,
+	.fw_info_get_run_version = intel_cmd_fw_info_get_run_version,
+	.fw_info_get_updated_version = intel_cmd_fw_info_get_updated_version,
+	.new_fw_start_update = intel_dimm_cmd_new_fw_start,
+	.fw_start_get_context = intel_cmd_fw_start_get_context,
+	.new_fw_send = intel_dimm_cmd_new_fw_send,
+	.new_fw_finish = intel_dimm_cmd_new_fw_finish,
+	.new_fw_abort = intel_dimm_cmd_new_fw_abort,
+	.new_fw_finish_query = intel_dimm_cmd_new_fw_finish_query,
+	.fw_fquery_get_fw_rev = intel_cmd_fw_fquery_get_fw_rev,
+	.fw_xlat_firmware_status = intel_cmd_fw_xlat_firmware_status,
 };
diff --git a/ndctl/lib/intel.h b/ndctl/lib/intel.h
index 9e63985..ff414a4 100644
--- a/ndctl/lib/intel.h
+++ b/ndctl/lib/intel.h
@@ -5,6 +5,12 @@
 #define __INTEL_H__
 #define ND_INTEL_SMART 1
 #define ND_INTEL_SMART_THRESHOLD 2
+
+#define ND_INTEL_FW_GET_INFO 12
+#define ND_INTEL_FW_START_UPDATE 13
+#define ND_INTEL_FW_SEND_DATA 14
+#define ND_INTEL_FW_FINISH_UPDATE 15
+#define ND_INTEL_FW_FINISH_STATUS_QUERY 16
 #define ND_INTEL_SMART_SET_THRESHOLD 17
 
 #define ND_INTEL_SMART_HEALTH_VALID             (1 << 0)
@@ -71,12 +77,83 @@ struct nd_intel_smart_set_threshold {
 	__u32 status;
 } __attribute__((packed));
 
+struct nd_intel_fw_info {
+	__u32 status;
+	__u32 storage_size;
+	__u32 max_send_len;
+	__u32 query_interval;
+	__u32 max_query_time;
+	__u8 update_cap;
+	__u8 reserved[3];
+	__u32 fis_version;
+	__u64 run_version;
+	__u64 updated_version;
+} __attribute__((packed));
+
+struct nd_intel_fw_start {
+	__u32 status;
+	__u32 context;
+} __attribute__((packed));
+
+/* this one has the output first because the variable input data size */
+struct nd_intel_fw_send_data {
+	__u32 context;
+	__u32 offset;
+	__u32 length;
+	__u8 data[0];
+/* reserving last 4 bytes as status */
+/*	__u32 status; */
+} __attribute__((packed));
+
+struct nd_intel_fw_finish_update {
+	__u8 ctrl_flags;
+	__u8 reserved[3];
+	__u32 context;
+	__u32 status;
+} __attribute__((packed));
+
+struct nd_intel_fw_finish_query {
+	__u32 context;
+	__u32 status;
+	__u64 updated_fw_rev;
+} __attribute__((packed));
+
 struct nd_pkg_intel {
 	struct nd_cmd_pkg gen;
 	union {
 		struct nd_intel_smart smart;
 		struct nd_intel_smart_threshold	thresh;
 		struct nd_intel_smart_set_threshold set_thresh;
+		struct nd_intel_fw_info info;
+		struct nd_intel_fw_start start;
+		struct nd_intel_fw_send_data send;
+		struct nd_intel_fw_finish_update finish;
+		struct nd_intel_fw_finish_query fquery;
 	};
 };
+
+#define ND_INTEL_STATUS_MASK		0xffff
+#define ND_INTEL_STATUS_SUCCESS		0
+#define ND_INTEL_STATUS_NOTSUPP		1
+#define ND_INTEL_STATUS_NOTEXIST	2
+#define ND_INTEL_STATUS_INVALPARM	3
+#define ND_INTEL_STATUS_HWERR		4
+#define ND_INTEL_STATUS_RETRY		5
+#define ND_INTEL_STATUS_UNKNOWN		6
+#define ND_INTEL_STATUS_EXTEND		7
+#define ND_INTEL_STATUS_NORES		8
+#define ND_INTEL_STATUS_NOTREADY	9
+
+#define ND_INTEL_STATUS_EXTEND_MASK	0xffff0000
+#define ND_INTEL_STATUS_START_BUSY	0x10000
+#define ND_INTEL_STATUS_SEND_CTXINVAL	0x10000
+#define ND_INTEL_STATUS_FIN_CTXINVAL	0x10000
+#define ND_INTEL_STATUS_FIN_DONE	0x20000
+#define ND_INTEL_STATUS_FIN_BAD		0x30000
+#define ND_INTEL_STATUS_FIN_ABORTED	0x40000
+#define ND_INTEL_STATUS_FQ_CTXINVAL	0x10000
+#define ND_INTEL_STATUS_FQ_BUSY		0x20000
+#define ND_INTEL_STATUS_FQ_BAD		0x30000
+#define ND_INTEL_STATUS_FQ_ORDER	0x40000
+
 #endif /* __INTEL_H__ */
diff --git a/ndctl/lib/libndctl.sym b/ndctl/lib/libndctl.sym
index 2ace942..d35d3fd 100644
--- a/ndctl/lib/libndctl.sym
+++ b/ndctl/lib/libndctl.sym
@@ -322,4 +322,19 @@ global:
 	ndctl_cmd_smart_threshold_set_ctrl_temperature;
 	ndctl_cmd_smart_threshold_set_spares;
 	ndctl_decode_smart_temperature;
+	ndctl_dimm_cmd_new_fw_get_info;
+	ndctl_dimm_cmd_new_fw_start_update;
+	ndctl_dimm_cmd_new_fw_send;
+	ndctl_dimm_cmd_new_fw_finish;
+	ndctl_dimm_cmd_new_fw_abort;
+	ndctl_dimm_cmd_new_fw_finish_query;
+	ndctl_cmd_fw_info_get_storage_size;
+	ndctl_cmd_fw_info_get_max_send_len;
+	ndctl_cmd_fw_info_get_query_interval;
+	ndctl_cmd_fw_info_get_max_query_time;
+	ndctl_cmd_fw_info_get_run_version;
+	ndctl_cmd_fw_info_get_updated_version;
+	ndctl_cmd_fw_start_get_context;
+	ndctl_cmd_fw_fquery_get_fw_rev;
+	ndctl_cmd_fw_xlat_firmware_status;
 } LIBNDCTL_13;
diff --git a/ndctl/lib/private.h b/ndctl/lib/private.h
index 6726097..26b56ea 100644
--- a/ndctl/lib/private.h
+++ b/ndctl/lib/private.h
@@ -303,6 +303,22 @@ struct ndctl_dimm_ops {
 	int (*smart_threshold_set_media_temperature)(struct ndctl_cmd *, unsigned int);
 	int (*smart_threshold_set_ctrl_temperature)(struct ndctl_cmd *, unsigned int);
 	int (*smart_threshold_set_spares)(struct ndctl_cmd *, unsigned int);
+	struct ndctl_cmd *(*new_fw_get_info)(struct ndctl_dimm *);
+	unsigned int (*fw_info_get_storage_size)(struct ndctl_cmd *);
+	unsigned int (*fw_info_get_max_send_len)(struct ndctl_cmd *);
+	unsigned int (*fw_info_get_query_interval)(struct ndctl_cmd *);
+	unsigned int (*fw_info_get_max_query_time)(struct ndctl_cmd *);
+	unsigned long long (*fw_info_get_run_version)(struct ndctl_cmd *);
+	unsigned long long (*fw_info_get_updated_version)(struct ndctl_cmd *);
+	struct ndctl_cmd *(*new_fw_start_update)(struct ndctl_dimm *);
+	unsigned int (*fw_start_get_context)(struct ndctl_cmd *);
+	struct ndctl_cmd *(*new_fw_send)(struct ndctl_cmd *,
+			unsigned int, unsigned int, void *);
+	struct ndctl_cmd *(*new_fw_finish)(struct ndctl_cmd *);
+	struct ndctl_cmd *(*new_fw_abort)(struct ndctl_cmd *);
+	struct ndctl_cmd *(*new_fw_finish_query)(struct ndctl_cmd *);
+	unsigned long long (*fw_fquery_get_fw_rev)(struct ndctl_cmd *);
+	enum ND_FW_STATUS (*fw_xlat_firmware_status)(struct ndctl_cmd *);
 };
 
 struct ndctl_dimm_ops * const intel_dimm_ops;
diff --git a/ndctl/libndctl.h b/ndctl/libndctl.h
index e9da20e..c02e8c7 100644
--- a/ndctl/libndctl.h
+++ b/ndctl/libndctl.h
@@ -15,6 +15,7 @@
 
 #include <stdbool.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <unistd.h>
 #include <errno.h>
 
@@ -581,6 +582,41 @@ int ndctl_dax_delete(struct ndctl_dax *dax);
 int ndctl_dax_is_configured(struct ndctl_dax *dax);
 struct daxctl_region *ndctl_dax_get_daxctl_region(struct ndctl_dax *dax);
 
+enum ND_FW_STATUS {
+	FW_SUCCESS = 0,		/* success */
+	FW_ENOTSUPP,		/* not supported */
+	FW_ENOTEXIST,		/* device not exist */
+	FW_EINVAL,		/* invalid input */
+	FW_EHWERR,		/* hardware error */
+	FW_ERETRY,		/* try again */
+	FW_EUNKNOWN,		/* unknown reason */
+	FW_ENORES,		/* out of resource */
+	FW_ENOTREADY,		/* hardware not ready */
+	FW_EBUSY,		/* firmware inprogress */
+	FW_EINVAL_CTX,		/* invalid context passed in */
+	FW_ALREADY_DONE,	/* firmware already updated */
+	FW_EBADFW,		/* firmware failed verification */
+	FW_ABORTED,		/* update sequence aborted success */
+	FW_ESEQUENCE,		/* update sequence incorrect */
+};
+
+struct ndctl_cmd *ndctl_dimm_cmd_new_fw_get_info(struct ndctl_dimm *dimm);
+struct ndctl_cmd *ndctl_dimm_cmd_new_fw_start_update(struct ndctl_dimm *dimm);
+struct ndctl_cmd *ndctl_dimm_cmd_new_fw_send(struct ndctl_cmd *start,
+		unsigned int offset, unsigned int len, void *data);
+struct ndctl_cmd *ndctl_dimm_cmd_new_fw_finish(struct ndctl_cmd *start);
+struct ndctl_cmd *ndctl_dimm_cmd_new_fw_abort(struct ndctl_cmd *start);
+struct ndctl_cmd *ndctl_dimm_cmd_new_fw_finish_query(struct ndctl_cmd *start);
+unsigned int ndctl_cmd_fw_info_get_storage_size(struct ndctl_cmd *cmd);
+unsigned int ndctl_cmd_fw_info_get_max_send_len(struct ndctl_cmd *cmd);
+unsigned int ndctl_cmd_fw_info_get_query_interval(struct ndctl_cmd *cmd);
+unsigned int ndctl_cmd_fw_info_get_max_query_time(struct ndctl_cmd *cmd);
+unsigned long long ndctl_cmd_fw_info_get_run_version(struct ndctl_cmd *cmd);
+unsigned long long ndctl_cmd_fw_info_get_updated_version(struct ndctl_cmd *cmd);
+unsigned int ndctl_cmd_fw_start_get_context(struct ndctl_cmd *cmd);
+unsigned long long ndctl_cmd_fw_fquery_get_fw_rev(struct ndctl_cmd *cmd);
+enum ND_FW_STATUS ndctl_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 3/4] ndctl: add firmware update command option for ndctl
  2017-12-15 23:23 [PATCH v3 0/4] ndctl: add DIMM firmware update support Dave Jiang
  2017-12-15 23:24 ` [PATCH v3 1/4] ndctl: add support to alloc_intel_cmd for variable payload Dave Jiang
  2017-12-15 23:24 ` [PATCH v3 2/4] ndctl: add firmware download support functions in libndctl Dave Jiang
@ 2017-12-15 23:24 ` Dave Jiang
  2018-01-29 22:49   ` Vishal Verma
  2017-12-15 23:24 ` [PATCH v3 4/4] ndctl, test: firmware update unit test Dave Jiang
  3 siblings, 1 reply; 7+ messages in thread
From: Dave Jiang @ 2017-12-15 23:24 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

Adding option "update-firmware" to ndctl for update firmware support from
Intel DSM v1.6. ndctl update-firmware takes an option of -f for a firmware
binary and a -d for the DIMM name:
ndctl update-firmware -d nmem0 -f new_firmware.bin

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 ndctl/Makefile.am |    3 
 ndctl/ndctl.c     |    1 
 ndctl/update.c    |  554 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 557 insertions(+), 1 deletion(-)
 create mode 100644 ndctl/update.c

diff --git a/Documentation/ndctl/Makefile.am b/Documentation/ndctl/Makefile.am
index 615baf0..27b2076 100644
--- a/Documentation/ndctl/Makefile.am
+++ b/Documentation/ndctl/Makefile.am
@@ -31,6 +31,7 @@ man1_MANS = \
 	ndctl-destroy-namespace.1 \
 	ndctl-check-namespace.1 \
 	ndctl-inject-error.1 \
+	ndctl-update-firmware.1 \
 	ndctl-list.1
 
 CLEANFILES = $(man1_MANS)
diff --git a/Documentation/ndctl/ndctl-update-firmware.txt b/Documentation/ndctl/ndctl-update-firmware.txt
new file mode 100644
index 0000000..d742302
--- /dev/null
+++ b/Documentation/ndctl/ndctl-update-firmware.txt
@@ -0,0 +1,18 @@
+ndctl-update-firmware(1)
+========================
+
+NAME
+----
+ndctl-update-firmware - provides updating of NVDIMM firmware
+
+SYNOPSIS
+--------
+[verse]
+'ndctl update-firmware' -f <firmware_file> -d <dimm name>
+
+COPYRIGHT
+---------
+Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
+version 2 <http://gnu.org/licenses/gpl.html>.  This is free software:
+you are free to change and redistribute it.  There is NO WARRANTY, to
+the extent permitted by law.
diff --git a/builtin.h b/builtin.h
index 5e1b7ef..1f423dc 100644
--- a/builtin.h
+++ b/builtin.h
@@ -43,4 +43,5 @@ int cmd_test(int argc, const char **argv, void *ctx);
 #ifdef ENABLE_DESTRUCTIVE
 int cmd_bat(int argc, const char **argv, void *ctx);
 #endif
+int cmd_update_firmware(int argc, const char **argv, void *ctx);
 #endif /* _NDCTL_BUILTIN_H_ */
diff --git a/ndctl/Makefile.am b/ndctl/Makefile.am
index 6677607..5cd8678 100644
--- a/ndctl/Makefile.am
+++ b/ndctl/Makefile.am
@@ -13,7 +13,8 @@ ndctl_SOURCES = ndctl.c \
 		test.c \
 		../util/json.c \
 		util/json-smart.c \
-		inject-error.c
+		inject-error.c \
+		update.c
 
 if ENABLE_DESTRUCTIVE
 ndctl_SOURCES += ../test/blk_namespaces.c \
diff --git a/ndctl/ndctl.c b/ndctl/ndctl.c
index 0f748e1..a0e5153 100644
--- a/ndctl/ndctl.c
+++ b/ndctl/ndctl.c
@@ -84,6 +84,7 @@ static struct cmd_struct commands[] = {
 	{ "init-labels", cmd_init_labels },
 	{ "check-labels", cmd_check_labels },
 	{ "inject-error", cmd_inject_error },
+	{ "update-firmware", cmd_update_firmware },
 	{ "list", cmd_list },
 	{ "help", cmd_help },
 	#ifdef ENABLE_TEST
diff --git a/ndctl/update.c b/ndctl/update.c
new file mode 100644
index 0000000..8d33b6a
--- /dev/null
+++ b/ndctl/update.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2017, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
+ * more details.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <util/log.h>
+#include <util/size.h>
+#include <util/util.h>
+#include <uuid/uuid.h>
+#include <util/json.h>
+#include <util/filter.h>
+#include <json-c/json.h>
+#include <util/fletcher.h>
+#include <ndctl/libndctl.h>
+#include <ndctl/namespace.h>
+#include <util/parse-options.h>
+#include <ccan/minmax/minmax.h>
+#include <ccan/array_size/array_size.h>
+#ifdef HAVE_NDCTL_H
+#include <linux/ndctl.h>
+#else
+#include <ndctl.h>
+#endif
+
+#include <libndctl-nfit.h>
+#include "private.h"
+#include <builtin.h>
+#include <test.h>
+
+#define ND_CMD_STATUS_SUCCESS	0
+#define ND_CMD_STATUS_NOTSUPP	1
+#define	ND_CMD_STATUS_NOTEXIST	2
+#define ND_CMD_STATUS_INVALPARM	3
+#define ND_CMD_STATUS_HWERR	4
+#define ND_CMD_STATUS_RETRY	5
+#define ND_CMD_STATUS_UNKNOWN	6
+#define ND_CMD_STATUS_EXTEND	7
+#define ND_CMD_STATUS_NORES	8
+#define ND_CMD_STATUS_NOTREADY	9
+
+#define ND_CMD_STATUS_START_BUSY	0x10000
+#define ND_CMD_STATUS_SEND_CTXINVAL	0x10000
+#define ND_CMD_STATUS_FIN_CTXINVAL	0x10000
+#define ND_CMD_STATUS_FIN_DONE		0x20000
+#define ND_CMD_STATUS_FIN_BAD		0x30000
+#define ND_CMD_STATUS_FIN_ABORTED	0x40000
+#define ND_CMD_STATUS_FQ_CTXINVAL	0x10000
+#define ND_CMD_STATUS_FQ_BUSY		0x20000
+#define ND_CMD_STATUS_FQ_BAD		0x30000
+#define ND_CMD_STATUS_FQ_ORDER		0x40000
+
+struct fw_info {
+	uint32_t store_size;
+	uint32_t update_size;
+	uint32_t query_interval;
+	uint32_t max_query;
+	uint32_t update_cap;
+	uint32_t fis_version;
+	uint64_t run_version;
+	uint32_t context;
+};
+
+struct update_context {
+	int fw_fd;
+	size_t fw_size;
+	const char *fw_path;
+	const char *dimm_id;
+	struct ndctl_dimm *dimm;
+	struct fw_info dimm_fw;
+	struct ndctl_cmd *start;
+};
+
+/*
+ * updating firmware consists of performing the following steps:
+ * 1. Call GET_FIMRWARE_INFO DSM. The return results provide:
+ *	A. Size of the firmware storage area
+ *	B. Max size per send command
+ *	C. Polling interval for check finish status
+ *	D. Max time for finish update poll
+ *	E. Update capabilities
+ *	F. Running FIS version
+ *	G. Running FW revision
+ *	H. Updated FW revision. Only valid after firmware update done.
+ * 2. Call START_FW_UPDATE. The return results provide:
+ *	A. Ready to start status
+ *	B. Valid FW update context
+ * 3. Call SEND_FW_UPDATE_DATA with valid payload
+ *    Repeat until done.
+ * 4. Call FINISH_FW_UPDATE
+ * 5. Poll with QUERY_FINISH_UPDATE success or failure
+ */
+
+static int verify_fw_size(struct update_context *uctx)
+{
+	struct fw_info *fw = &uctx->dimm_fw;
+
+	if (uctx->fw_size > fw->store_size) {
+		error("Firmware file size greater than DIMM store\n");
+		return -E2BIG;
+	}
+
+	return 0;
+}
+
+static int submit_get_firmware_info(struct update_context *uctx)
+{
+	struct ndctl_cmd *cmd;
+	int rc;
+	enum ND_FW_STATUS status;
+	struct fw_info *fw = &uctx->dimm_fw;
+
+	cmd = ndctl_dimm_cmd_new_fw_get_info(uctx->dimm);
+	if (!cmd)
+		return -ENXIO;
+
+	rc = ndctl_cmd_submit(cmd);
+	if (rc < 0)
+		return rc;
+
+	status = ndctl_cmd_fw_xlat_firmware_status(cmd);
+	if (status != FW_SUCCESS) {
+		error("GET FIRMWARE INFO failed: %#x\n", status);
+		return -ENXIO;
+	}
+
+	fw->store_size = ndctl_cmd_fw_info_get_storage_size(cmd);
+	if (fw->store_size == UINT_MAX)
+		return -ENXIO;
+
+	fw->update_size = ndctl_cmd_fw_info_get_max_send_len(cmd);
+	if (fw->update_size == UINT_MAX)
+		return -ENXIO;
+
+	fw->query_interval = ndctl_cmd_fw_info_get_query_interval(cmd);
+	if (fw->query_interval == UINT_MAX)
+		return -ENXIO;
+
+	fw->max_query = ndctl_cmd_fw_info_get_max_query_time(cmd);
+	if (fw->max_query == UINT_MAX)
+		return -ENXIO;
+
+	fw->update_cap = ndctl_cmd_fw_info_get_update_cap(cmd);
+	if (fw->update_cap == UINT_MAX)
+		return -ENXIO;
+
+	fw->fis_version = ndctl_cmd_fw_info_get_fis_version(cmd);
+	if (fw->fis_version == UINT_MAX)
+		return -ENXIO;
+
+	fw->run_version = ndctl_cmd_fw_info_get_run_version(cmd);
+	if (fw->run_version == ULLONG_MAX)
+		return -ENXIO;
+
+	rc = verify_fw_size(uctx);
+	ndctl_cmd_unref(cmd);
+	return rc;
+}
+
+static int submit_start_firmware_upload(struct update_context *uctx)
+{
+	struct ndctl_cmd *cmd;
+	int rc;
+	enum ND_FW_STATUS status;
+	struct fw_info *fw = &uctx->dimm_fw;
+
+	cmd = ndctl_dimm_cmd_new_fw_start_update(uctx->dimm);
+	if (!cmd)
+		return -ENXIO;
+
+	rc = ndctl_cmd_submit(cmd);
+	if (rc < 0)
+		return rc;
+
+	status = ndctl_cmd_fw_xlat_firmware_status(cmd);
+	if (status != FW_SUCCESS) {
+		error("START FIRMWARE UPDATE failed: %#x\n", status);
+		if (status == FW_EBUSY)
+			error("Another firmware upload in progress or finished.\n");
+		return -ENXIO;
+	}
+
+	fw->context = ndctl_cmd_fw_start_get_context(cmd);
+	if (fw->context == UINT_MAX)
+		return -ENXIO;
+
+	uctx->start = cmd;
+
+	return 0;
+}
+
+static int get_fw_data_from_file(int fd, void *buf, uint32_t len,
+		uint32_t offset)
+{
+	ssize_t rc, total = len;
+
+	while (len) {
+		rc = pread(fd, buf, len, offset);
+		if (rc < 0)
+			return -errno;
+		len -= rc;
+	}
+
+	return total;
+}
+
+static int send_firmware(struct update_context *uctx)
+{
+	struct ndctl_cmd *cmd = NULL;
+	ssize_t read;
+	int rc;
+	enum ND_FW_STATUS status;
+	struct fw_info *fw = &uctx->dimm_fw;
+	uint32_t copied = 0, len, remain;
+	void *buf;
+
+	buf = malloc(fw->update_size);
+	if (!buf)
+		return -ENOMEM;
+
+	remain = uctx->fw_size;
+
+	while (remain) {
+		len = min(fw->update_size, remain);
+		read = get_fw_data_from_file(uctx->fw_fd, buf, len, copied);
+		if (read < 0) {
+			rc = read;
+			goto cleanup;
+		}
+
+		cmd = ndctl_dimm_cmd_new_fw_send(uctx->start, copied, read,
+				buf);
+		if (!cmd) {
+			rc = -ENXIO;
+			goto cleanup;
+		}
+
+		rc = ndctl_cmd_submit(cmd);
+		if (rc < 0)
+			goto cleanup;
+
+		status = ndctl_cmd_fw_xlat_firmware_status(cmd);
+		if (status != FW_SUCCESS) {
+			error("SEND FIRMWARE failed: %#x\n", status);
+			rc = -ENXIO;
+			goto cleanup;
+		}
+
+		copied += read;
+		remain -= read;
+
+		ndctl_cmd_unref(cmd);
+		cmd = NULL;
+	}
+
+cleanup:
+	if (cmd)
+		ndctl_cmd_unref(cmd);
+	free(buf);
+	return rc;
+}
+
+static int submit_finish_firmware(struct update_context *uctx)
+{
+	struct ndctl_cmd *cmd;
+	int rc;
+	enum ND_FW_STATUS status;
+
+	cmd = ndctl_dimm_cmd_new_fw_finish(uctx->start);
+	if (!cmd)
+		return -ENXIO;
+
+	rc = ndctl_cmd_submit(cmd);
+	if (rc < 0)
+		goto out;
+
+	status = ndctl_cmd_fw_xlat_firmware_status(cmd);
+	if (status != FW_SUCCESS) {
+		error("FINISH FIRMWARE UPDATE failed: %#x\n", status);
+		rc = -ENXIO;
+		goto out;
+	}
+
+out:
+	ndctl_cmd_unref(cmd);
+	return rc;
+}
+
+static int submit_abort_firmware(struct update_context *uctx)
+{
+	struct ndctl_cmd *cmd;
+	int rc;
+	enum ND_FW_STATUS status;
+
+	cmd = ndctl_dimm_cmd_new_fw_abort(uctx->start);
+	if (!cmd)
+		return -ENXIO;
+
+	rc = ndctl_cmd_submit(cmd);
+	if (rc < 0)
+		goto out;
+
+	status = ndctl_cmd_fw_xlat_firmware_status(cmd);
+	if (!(status & ND_CMD_STATUS_FIN_ABORTED)) {
+		error("FW update abort failed: %#x\n", status);
+		rc = -ENXIO;
+		goto out;
+	}
+
+out:
+	ndctl_cmd_unref(cmd);
+	return rc;
+}
+
+static int query_fw_finish_status(struct update_context *uctx)
+{
+	struct ndctl_cmd *cmd;
+	int rc;
+	enum ND_FW_STATUS status;
+	struct fw_info *fw = &uctx->dimm_fw;
+	bool done = false;
+	struct timeval before, after;
+	struct timespec now;
+	uint64_t ver;
+
+	cmd = ndctl_dimm_cmd_new_fw_finish_query(uctx->start);
+	if (!cmd)
+		return -ENXIO;
+
+	rc = gettimeofday(&before, NULL);
+	if (rc < 0)
+		goto out;
+
+	now.tv_nsec = fw->query_interval / 1000;
+	now.tv_sec = 0;
+
+	do {
+		rc = ndctl_cmd_submit(cmd);
+		if (rc < 0)
+			break;
+
+		status = ndctl_cmd_fw_xlat_firmware_status(cmd);
+		switch (status) {
+		case FW_SUCCESS:
+			ver = ndctl_cmd_fw_fquery_get_fw_rev(cmd);
+			if (ver == 0) {
+				printf("No firmware updated\n");
+				rc = -ENXIO;
+				goto out;
+			}
+
+			printf("Image %s updated successfully to DIMM %s\n",
+					uctx->fw_path, uctx->dimm_id);
+			printf("Firmware version %#lx.\n", ver);
+			printf("Reboot to activate.\n");
+			done = true;
+			rc = 0;
+			break;
+		case FW_EBUSY:
+			/* Still on going, continue */
+			rc = gettimeofday(&after, NULL);
+			if (rc < 0) {
+				rc = -errno;
+				goto out;
+			}
+
+			/*
+			 * If we expire max query time,
+			 * we timed out
+			 */
+			if (after.tv_sec - before.tv_sec >
+					fw->max_query / 1000000) {
+				rc = -ETIMEDOUT;
+				goto out;
+			}
+
+			/*
+			 * Sleep the interval dictated by firmware
+			 * before query again.
+			 */
+			rc = nanosleep(&now, NULL);
+			if (rc < 0) {
+				rc = -errno;
+				goto out;
+			}
+			break;
+		case FW_EBADFW:
+			printf("Image failed to verify by DIMM\n");
+		case FW_EINVAL_CTX:
+		case FW_ESEQUENCE:
+			done = true;
+			rc = -ENXIO;
+			goto out;
+		case FW_ENORES:
+			printf("Firmware update sequence timed out\n");
+			rc = -ETIMEDOUT;
+			done = true;
+			goto out;
+		default:
+			rc = -EINVAL;
+			done = true;
+			goto out;
+		}
+	} while (!done);
+
+out:
+	ndctl_cmd_unref(cmd);
+	return rc;
+}
+
+static int update_firmware(struct update_context *uctx)
+{
+	int rc;
+
+	rc = submit_get_firmware_info(uctx);
+	if (rc < 0)
+		return rc;
+
+	rc = submit_start_firmware_upload(uctx);
+	if (rc < 0)
+		return rc;
+
+	printf("Uploading %s to DIMM %s\n", uctx->fw_path, uctx->dimm_id);
+
+	rc = send_firmware(uctx);
+	if (rc < 0) {
+		error("Firmware send failed. Aborting...\n");
+		rc = submit_abort_firmware(uctx);
+		if (rc < 0)
+			error("Aborting update sequence failed\n");
+		return rc;
+	}
+
+	rc = submit_finish_firmware(uctx);
+	if (rc < 0) {
+		error("Unable to end update sequence\n");
+		rc = submit_abort_firmware(uctx);
+		if (rc < 0)
+			error("Aborting update sequence failed\n");
+		return rc;
+	}
+
+	rc = query_fw_finish_status(uctx);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static int get_ndctl_dimm(struct update_context *uctx, void *ctx)
+{
+	struct ndctl_dimm *dimm;
+	struct ndctl_bus *bus;
+
+	ndctl_bus_foreach(ctx, bus)
+		ndctl_dimm_foreach(bus, dimm) {
+			if (!util_dimm_filter(dimm, uctx->dimm_id))
+				continue;
+			uctx->dimm = dimm;
+			return 0;
+		}
+
+	return -ENODEV;
+}
+
+static int verify_fw_file(struct update_context *uctx)
+{
+	struct stat st;
+
+	if (stat(uctx->fw_path, &st) < 0)
+		return -errno;
+	if (!S_ISREG(st.st_mode))
+		return -EINVAL;
+
+	uctx->fw_size = st.st_size;
+	if (uctx->fw_size == 0)
+		return -EINVAL;
+
+	uctx->fw_fd = open(uctx->fw_path, O_RDONLY);
+	if (uctx->fw_fd < 0)
+		return -errno;
+
+	return 0;
+}
+
+int cmd_update_firmware(int argc, const char **argv, void *ctx)
+{
+	struct update_context uctx = { 0 };
+	const struct option options[] = {
+		OPT_STRING('f', "firmware", &uctx.fw_path,
+				"file-name", "name of firmware"),
+		OPT_STRING('d', "dimm", &uctx.dimm_id, "dimm-id",
+				"dimm to be updated"),
+		OPT_END(),
+	};
+	const char * const u[] = {
+		"ndctl update_firmware [<options>]",
+		NULL
+	};
+	int i, rc;
+
+	argc = parse_options(argc, argv, options, u, 0);
+	for (i = 0; i < argc; i++)
+		error("unknown parameter \"%s\"\n", argv[i]);
+	if (argc)
+		usage_with_options(u, options);
+
+	if (!uctx.fw_path) {
+		error("No firmware file provided\n");
+		usage_with_options(u, options);
+		return -EINVAL;
+	}
+
+	if (!uctx.dimm_id) {
+		error("No DIMM ID provided\n");
+		usage_with_options(u, options);
+		return -EINVAL;
+	}
+
+	rc = verify_fw_file(&uctx);
+	if (rc < 0)
+		return rc;
+
+	rc = get_ndctl_dimm(&uctx, ctx);
+	if (rc < 0)
+		return rc;
+
+	rc = update_firmware(&uctx);
+	if (rc < 0)
+		return rc;
+
+	if (uctx.start)
+		ndctl_cmd_unref(uctx.start);
+
+	return 0;
+}

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 4/4] ndctl, test: firmware update unit test
  2017-12-15 23:23 [PATCH v3 0/4] ndctl: add DIMM firmware update support Dave Jiang
                   ` (2 preceding siblings ...)
  2017-12-15 23:24 ` [PATCH v3 3/4] ndctl: add firmware update command option for ndctl Dave Jiang
@ 2017-12-15 23:24 ` Dave Jiang
  2018-01-29 22:55   ` Vishal Verma
  3 siblings, 1 reply; 7+ messages in thread
From: Dave Jiang @ 2017-12-15 23:24 UTC (permalink / raw)
  To: dan.j.williams; +Cc: linux-nvdimm

Adding a unit test that will use nfit_test kernel module to test the
firmware update sequence.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 ndctl/update.c |   10 ----------
 1 file changed, 10 deletions(-)

diff --git a/ndctl/update.c b/ndctl/update.c
index 8d33b6a..a9400d6 100644
--- a/ndctl/update.c
+++ b/ndctl/update.c
@@ -73,8 +73,6 @@ struct fw_info {
 	uint32_t update_size;
 	uint32_t query_interval;
 	uint32_t max_query;
-	uint32_t update_cap;
-	uint32_t fis_version;
 	uint64_t run_version;
 	uint32_t context;
 };
@@ -158,14 +156,6 @@ static int submit_get_firmware_info(struct update_context *uctx)
 	if (fw->max_query == UINT_MAX)
 		return -ENXIO;
 
-	fw->update_cap = ndctl_cmd_fw_info_get_update_cap(cmd);
-	if (fw->update_cap == UINT_MAX)
-		return -ENXIO;
-
-	fw->fis_version = ndctl_cmd_fw_info_get_fis_version(cmd);
-	if (fw->fis_version == UINT_MAX)
-		return -ENXIO;
-
 	fw->run_version = ndctl_cmd_fw_info_get_run_version(cmd);
 	if (fw->run_version == ULLONG_MAX)
 		return -ENXIO;
diff --git a/test/Makefile.am b/test/Makefile.am
index d4c2bd6..d5ef648 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -16,7 +16,8 @@ TESTS =\
 	blk-exhaust.sh \
 	sector-mode.sh \
 	inject-error.sh \
-	btt-errors.sh
+	btt-errors.sh \
+	firmware-update.sh
 
 check_PROGRAMS =\
 	libndctl \
diff --git a/test/firmware-update.sh b/test/firmware-update.sh
new file mode 100755
index 0000000..ff777b9
--- /dev/null
+++ b/test/firmware-update.sh
@@ -0,0 +1,74 @@
+#!/bin/bash -Ex
+
+# Copyright(c) 2017 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+
+[ -f "../ndctl/ndctl" ] && [ -x "../ndctl/ndctl" ] && ndctl="../ndctl/ndctl"
+[ -f "./ndctl/ndctl" ] && [ -x "./ndctl/ndctl" ] && ndctl="./ndctl/ndctl"
+[ -z "$ndctl" ] && echo "Couldn't find an ndctl binary" && exit 1
+bus="nfit_test.0"
+json2var="s/[{}\",]//g; s/:/=/g"
+rc=77
+dev=""
+image="update-fw.img"
+
+trap 'err $LINENO' ERR
+
+# $1: Line number
+# $2: exit code
+err()
+{
+	[ -n "$2" ] && rc="$2"
+	echo "test/firmware-update.sh: failed at line $1"
+	exit "$rc"
+}
+
+check_min_kver()
+{
+	local ver="$1"
+	: "${KVER:=$(uname -r)}"
+
+	[ -n "$ver" ] || return 1
+	[[ "$ver" == "$(echo -e "$ver\n$KVER" | sort -V | head -1)" ]]
+}
+
+reset()
+{
+	$ndctl disable-region -b "$bus" all
+	$ndctl zero-labels -b "$bus" all
+	$ndctl enable-region -b "$bus" all
+	if [ -f $image ]; then
+		rm -f $image
+	fi
+}
+
+detect()
+{
+	dev=$($ndctl list -b "$bus" -D | jq .[0].dev | tr -d '"')
+	[ -n "$dev" ] || err "$LINENO" 2
+}
+
+do_tests()
+{
+	fallocate -l 196608 $image
+	$ndctl update-firmware -d $dev -f $image
+}
+
+check_min_kver "4.16" || { echo "kernel $KVER may lack firmware update test handling"; exit
+$rc; }
+modprobe nfit_test
+rc=1
+reset
+detect
+do_tests
+reset
+exit 0
+

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 3/4] ndctl: add firmware update command option for ndctl
  2017-12-15 23:24 ` [PATCH v3 3/4] ndctl: add firmware update command option for ndctl Dave Jiang
@ 2018-01-29 22:49   ` Vishal Verma
  0 siblings, 0 replies; 7+ messages in thread
From: Vishal Verma @ 2018-01-29 22:49 UTC (permalink / raw)
  To: Dave Jiang; +Cc: linux-nvdimm

On 12/15, Dave Jiang wrote:
> Adding option "update-firmware" to ndctl for update firmware support from
> Intel DSM v1.6. ndctl update-firmware takes an option of -f for a firmware
> binary and a -d for the DIMM name:
> ndctl update-firmware -d nmem0 -f new_firmware.bin
> 
> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  ndctl/Makefile.am |    3 
>  ndctl/ndctl.c     |    1 
>  ndctl/update.c    |  554 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 557 insertions(+), 1 deletion(-)
>  create mode 100644 ndctl/update.c
> 

This mostly looks fine, a few comments below:

<..>

> diff --git a/ndctl/update.c b/ndctl/update.c
> new file mode 100644
> index 0000000..8d33b6a
> --- /dev/null
> +++ b/ndctl/update.c
> @@ -0,0 +1,554 @@
> +/*
> + * Copyright (c) 2017, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU Lesser General Public License,
> + * version 2.1, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT ANY
> + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
> + * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
> + * more details.
> + */

It looks like we are switching to SPDK license identifiers for new
files, similar to the kernel. So perhaps this can be replaced with:
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2018 Intel Corporation. All rights reserved. */

<..>

> +
> +static int verify_fw_size(struct update_context *uctx)
> +{
> +	struct fw_info *fw = &uctx->dimm_fw;
> +
> +	if (uctx->fw_size > fw->store_size) {
> +		error("Firmware file size greater than DIMM store\n");
> +		return -E2BIG;

E2BIG seems to be "argument list too long". Perhaps ENOSPC is a better
choice here?

> +	}
> +
> +	return 0;
> +}
> +
> 

<..>

> +static int query_fw_finish_status(struct update_context *uctx)
> +{
> +	struct ndctl_cmd *cmd;
> +	int rc;
> +	enum ND_FW_STATUS status;
> +	struct fw_info *fw = &uctx->dimm_fw;
> +	bool done = false;
> +	struct timeval before, after;
> +	struct timespec now;
> +	uint64_t ver;
> +
> +	cmd = ndctl_dimm_cmd_new_fw_finish_query(uctx->start);
> +	if (!cmd)
> +		return -ENXIO;
> +
> +	rc = gettimeofday(&before, NULL);
> +	if (rc < 0)
> +		goto out;
> +

I think using gettimeofday is generally discouraged; we should use
clock_gettime instead for this sort of thing.

> +	now.tv_nsec = fw->query_interval / 1000;
> +	now.tv_sec = 0;
> +
> +	do {
> +		rc = ndctl_cmd_submit(cmd);
> +		if (rc < 0)
> +			break;
> +
> +		status = ndctl_cmd_fw_xlat_firmware_status(cmd);
> +		switch (status) {
> +		case FW_SUCCESS:
> +			ver = ndctl_cmd_fw_fquery_get_fw_rev(cmd);
> +			if (ver == 0) {
> +				printf("No firmware updated\n");
> +				rc = -ENXIO;
> +				goto out;
> +			}
> +
> +			printf("Image %s updated successfully to DIMM %s\n",
> +					uctx->fw_path, uctx->dimm_id);
> +			printf("Firmware version %#lx.\n", ver);
> +			printf("Reboot to activate.\n");
> +			done = true;
> +			rc = 0;
> +			break;
> +		case FW_EBUSY:
> +			/* Still on going, continue */
> +			rc = gettimeofday(&after, NULL);
> +			if (rc < 0) {
> +				rc = -errno;
> +				goto out;
> +			}

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 4/4] ndctl, test: firmware update unit test
  2017-12-15 23:24 ` [PATCH v3 4/4] ndctl, test: firmware update unit test Dave Jiang
@ 2018-01-29 22:55   ` Vishal Verma
  0 siblings, 0 replies; 7+ messages in thread
From: Vishal Verma @ 2018-01-29 22:55 UTC (permalink / raw)
  To: Dave Jiang; +Cc: linux-nvdimm

On 12/15, Dave Jiang wrote:
> Adding a unit test that will use nfit_test kernel module to test the
> firmware update sequence.
> 
> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  ndctl/update.c |   10 ----------

This diffstat is missing the new file? But my attention only went to
that because..

>  1 file changed, 10 deletions(-)
> 
> diff --git a/ndctl/update.c b/ndctl/update.c
> index 8d33b6a..a9400d6 100644
> --- a/ndctl/update.c
> +++ b/ndctl/update.c
> @@ -73,8 +73,6 @@ struct fw_info {
>  	uint32_t update_size;
>  	uint32_t query_interval;
>  	uint32_t max_query;
> -	uint32_t update_cap;
> -	uint32_t fis_version;
>  	uint64_t run_version;
>  	uint32_t context;
>  };
> @@ -158,14 +156,6 @@ static int submit_get_firmware_info(struct update_context *uctx)
>  	if (fw->max_query == UINT_MAX)
>  		return -ENXIO;
>  
> -	fw->update_cap = ndctl_cmd_fw_info_get_update_cap(cmd);
> -	if (fw->update_cap == UINT_MAX)
> -		return -ENXIO;
> -
> -	fw->fis_version = ndctl_cmd_fw_info_get_fis_version(cmd);
> -	if (fw->fis_version == UINT_MAX)
> -		return -ENXIO;
> -

..I think you applied the removal fixup for these two things to the
wrong patch? i.e. they are being added in patch 3 but removed in 4.

>  	fw->run_version = ndctl_cmd_fw_info_get_run_version(cmd);
>  	if (fw->run_version == ULLONG_MAX)
>  		return -ENXIO;
> diff --git a/test/Makefile.am b/test/Makefile.am
> index d4c2bd6..d5ef648 100644
> --- a/test/Makefile.am
> +++ b/test/Makefile.am
> @@ -16,7 +16,8 @@ TESTS =\
>  	blk-exhaust.sh \
>  	sector-mode.sh \
>  	inject-error.sh \
> -	btt-errors.sh
> +	btt-errors.sh \
> +	firmware-update.sh
>  
>  check_PROGRAMS =\
>  	libndctl \
> diff --git a/test/firmware-update.sh b/test/firmware-update.sh
> new file mode 100755
> index 0000000..ff777b9
> --- /dev/null
> +++ b/test/firmware-update.sh
> @@ -0,0 +1,74 @@
> +#!/bin/bash -Ex
> +
> +# Copyright(c) 2017 Intel Corporation. All rights reserved.
> +#
> +# This program is free software; you can redistribute it and/or modify it
> +# under the terms of version 2 of the GNU General Public License as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful, but
> +# WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +# General Public License for more details.
> +
> +[ -f "../ndctl/ndctl" ] && [ -x "../ndctl/ndctl" ] && ndctl="../ndctl/ndctl"
> +[ -f "./ndctl/ndctl" ] && [ -x "./ndctl/ndctl" ] && ndctl="./ndctl/ndctl"
> +[ -z "$ndctl" ] && echo "Couldn't find an ndctl binary" && exit 1
> +bus="nfit_test.0"
> +json2var="s/[{}\",]//g; s/:/=/g"
> +rc=77
> +dev=""
> +image="update-fw.img"
> +
> +trap 'err $LINENO' ERR
> +
> +# $1: Line number
> +# $2: exit code
> +err()
> +{
> +	[ -n "$2" ] && rc="$2"
> +	echo "test/firmware-update.sh: failed at line $1"
> +	exit "$rc"
> +}
> +
> +check_min_kver()
> +{
> +	local ver="$1"
> +	: "${KVER:=$(uname -r)}"
> +
> +	[ -n "$ver" ] || return 1
> +	[[ "$ver" == "$(echo -e "$ver\n$KVER" | sort -V | head -1)" ]]
> +}
> +
> +reset()
> +{
> +	$ndctl disable-region -b "$bus" all
> +	$ndctl zero-labels -b "$bus" all
> +	$ndctl enable-region -b "$bus" all
> +	if [ -f $image ]; then
> +		rm -f $image
> +	fi
> +}
> +
> +detect()
> +{
> +	dev=$($ndctl list -b "$bus" -D | jq .[0].dev | tr -d '"')
> +	[ -n "$dev" ] || err "$LINENO" 2
> +}
> +
> +do_tests()
> +{
> +	fallocate -l 196608 $image
> +	$ndctl update-firmware -d $dev -f $image
> +}
> +
> +check_min_kver "4.16" || { echo "kernel $KVER may lack firmware update test handling"; exit
> +$rc; }
> +modprobe nfit_test
> +rc=1
> +reset
> +detect
> +do_tests
> +reset
> +exit 0
> +
> 
> _______________________________________________
> Linux-nvdimm mailing list
> Linux-nvdimm@lists.01.org
> https://lists.01.org/mailman/listinfo/linux-nvdimm
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-01-29 22:49 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-15 23:23 [PATCH v3 0/4] ndctl: add DIMM firmware update support Dave Jiang
2017-12-15 23:24 ` [PATCH v3 1/4] ndctl: add support to alloc_intel_cmd for variable payload Dave Jiang
2017-12-15 23:24 ` [PATCH v3 2/4] ndctl: add firmware download support functions in libndctl Dave Jiang
2017-12-15 23:24 ` [PATCH v3 3/4] ndctl: add firmware update command option for ndctl Dave Jiang
2018-01-29 22:49   ` Vishal Verma
2017-12-15 23:24 ` [PATCH v3 4/4] ndctl, test: firmware update unit test Dave Jiang
2018-01-29 22:55   ` Vishal Verma

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.