* [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.