On Fri, 2018-01-26 at 10:55 -0700, Dave Jiang wrote:
> Adding support in nfit_test for DSM v1.6 firmware update sequence.
> The test
> will simulate the flashing of firmware to the DIMM. A bogus version
> string
> will be returned as the test has no idea how to parse the firmware
> binary.
> Any bogus binary can be used to "update" as the actual binary is not
> copied
> into the kernel.
> 
> Signed-off-by: Dave Jiang <[email protected]>
> ---
>  tools/testing/nvdimm/test/nfit.c      |  293
> ++++++++++++++++++++++++++++++++-
>  tools/testing/nvdimm/test/nfit_test.h |   66 +++++++
>  2 files changed, 345 insertions(+), 14 deletions(-)
> 

For both of these,
Reviewed-by: Vishal Verma <[email protected]>

> diff --git a/tools/testing/nvdimm/test/nfit.c
> b/tools/testing/nvdimm/test/nfit.c
> index 2b57254342aa..3d999f193471 100644
> --- a/tools/testing/nvdimm/test/nfit.c
> +++ b/tools/testing/nvdimm/test/nfit.c
> @@ -137,6 +137,14 @@ static u32 handle[] = {
>  
>  static unsigned long dimm_fail_cmd_flags[NUM_DCR];
>  
> +struct nfit_test_fw {
> +     enum intel_fw_update_state state;
> +     u32 context;
> +     u64 version;
> +     u32 size_received;
> +     u64 end_time;
> +};
> +
>  struct nfit_test {
>       struct acpi_nfit_desc acpi_desc;
>       struct platform_device pdev;
> @@ -172,6 +180,7 @@ struct nfit_test {
>       struct nd_intel_smart_threshold *smart_threshold;
>       struct badrange badrange;
>       struct work_struct work;
> +     struct nfit_test_fw *fw;
>  };
>  
>  static struct workqueue_struct *nfit_wq;
> @@ -183,6 +192,226 @@ static struct nfit_test *to_nfit_test(struct
> device *dev)
>       return container_of(pdev, struct nfit_test, pdev);
>  }
>  
> +static int nd_intel_test_get_fw_info(struct nfit_test *t,
> +             struct nd_intel_fw_info *nd_cmd, unsigned int
> buf_len,
> +             int idx)
> +{
> +     struct device *dev = &t->pdev.dev;
> +     struct nfit_test_fw *fw = &t->fw[idx];
> +
> +     dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p, buf_len: %u, idx:
> %d\n",
> +                     __func__, t, nd_cmd, buf_len, idx);
> +
> +     if (buf_len < sizeof(*nd_cmd))
> +             return -EINVAL;
> +
> +     nd_cmd->status = 0;
> +     nd_cmd->storage_size = INTEL_FW_STORAGE_SIZE;
> +     nd_cmd->max_send_len = INTEL_FW_MAX_SEND_LEN;
> +     nd_cmd->query_interval = INTEL_FW_QUERY_INTERVAL;
> +     nd_cmd->max_query_time = INTEL_FW_QUERY_MAX_TIME;
> +     nd_cmd->update_cap = 0;
> +     nd_cmd->fis_version = INTEL_FW_FIS_VERSION;
> +     nd_cmd->run_version = 0;
> +     nd_cmd->updated_version = fw->version;
> +
> +     return 0;
> +}
> +
> +static int nd_intel_test_start_update(struct nfit_test *t,
> +             struct nd_intel_fw_start *nd_cmd, unsigned int
> buf_len,
> +             int idx)
> +{
> +     struct device *dev = &t->pdev.dev;
> +     struct nfit_test_fw *fw = &t->fw[idx];
> +
> +     dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx:
> %d)\n",
> +                     __func__, t, nd_cmd, buf_len, idx);
> +
> +     if (buf_len < sizeof(*nd_cmd))
> +             return -EINVAL;
> +
> +     if (fw->state != FW_STATE_NEW) {
> +             /* extended status, FW update in progress */
> +             nd_cmd->status = 0x10007;
> +             return 0;
> +     }
> +
> +     fw->state = FW_STATE_IN_PROGRESS;
> +     fw->context++;
> +     fw->size_received = 0;
> +     nd_cmd->status = 0;
> +     nd_cmd->context = fw->context;
> +
> +     dev_dbg(dev, "%s: context issued: %#x\n", __func__, nd_cmd-
> >context);
> +
> +     return 0;
> +}
> +
> +static int nd_intel_test_send_data(struct nfit_test *t,
> +             struct nd_intel_fw_send_data *nd_cmd, unsigned int
> buf_len,
> +             int idx)
> +{
> +     struct device *dev = &t->pdev.dev;
> +     struct nfit_test_fw *fw = &t->fw[idx];
> +     u32 *status = (u32 *)&nd_cmd->data[nd_cmd->length];
> +
> +     dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx:
> %d)\n",
> +                     __func__, t, nd_cmd, buf_len, idx);
> +
> +     if (buf_len < sizeof(*nd_cmd))
> +             return -EINVAL;
> +
> +
> +     dev_dbg(dev, "%s: cmd->status: %#x\n", __func__, *status);
> +     dev_dbg(dev, "%s: cmd->data[0]: %#x\n", __func__, nd_cmd-
> >data[0]);
> +     dev_dbg(dev, "%s: cmd->data[%u]: %#x\n", __func__, nd_cmd-
> >length-1,
> +                     nd_cmd->data[nd_cmd->length-1]);
> +
> +     if (fw->state != FW_STATE_IN_PROGRESS) {
> +             dev_dbg(dev, "%s: not in IN_PROGRESS state\n",
> __func__);
> +             *status = 0x5;
> +             return 0;
> +     }
> +
> +     if (nd_cmd->context != fw->context) {
> +             dev_dbg(dev, "%s: incorrect context: in: %#x
> correct: %#x\n",
> +                             __func__, nd_cmd->context, fw-
> >context);
> +             *status = 0x10007;
> +             return 0;
> +     }
> +
> +     /*
> +      * check offset + len > size of fw storage
> +      * check length is > max send length
> +      */
> +     if (nd_cmd->offset + nd_cmd->length > INTEL_FW_STORAGE_SIZE
> ||
> +                     nd_cmd->length > INTEL_FW_MAX_SEND_LEN) {
> +             *status = 0x3;
> +             dev_dbg(dev, "%s: buffer boundary violation\n",
> __func__);
> +             return 0;
> +     }
> +
> +     fw->size_received += nd_cmd->length;
> +     dev_dbg(dev, "%s: copying %u bytes, %u bytes so far\n",
> +                     __func__, nd_cmd->length, fw-
> >size_received);
> +     *status = 0;
> +     return 0;
> +}
> +
> +static int nd_intel_test_finish_fw(struct nfit_test *t,
> +             struct nd_intel_fw_finish_update *nd_cmd,
> +             unsigned int buf_len, int idx)
> +{
> +     struct device *dev = &t->pdev.dev;
> +     struct nfit_test_fw *fw = &t->fw[idx];
> +
> +     dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx:
> %d)\n",
> +                     __func__, t, nd_cmd, buf_len, idx);
> +
> +     if (fw->state == FW_STATE_UPDATED) {
> +             /* update already done, need cold boot */
> +             nd_cmd->status = 0x20007;
> +             return 0;
> +     }
> +
> +     dev_dbg(dev, "%s: context: %#x  ctrl_flags: %#x\n",
> +                     __func__, nd_cmd->context, nd_cmd-
> >ctrl_flags);
> +
> +     switch (nd_cmd->ctrl_flags) {
> +     case 0: /* finish */
> +             if (nd_cmd->context != fw->context) {
> +                     dev_dbg(dev, "%s: incorrect context: in: %#x
> correct: %#x\n",
> +                                     __func__, nd_cmd->context,
> +                                     fw->context);
> +                     nd_cmd->status = 0x10007;
> +                     return 0;
> +             }
> +             nd_cmd->status = 0;
> +             fw->state = FW_STATE_VERIFY;
> +             /* set 1 second of time for firmware "update" */
> +             fw->end_time = jiffies + HZ;
> +             break;
> +
> +     case 1: /* abort */
> +             fw->size_received = 0;
> +             /* successfully aborted status */
> +             nd_cmd->status = 0x40007;
> +             fw->state = FW_STATE_NEW;
> +             dev_dbg(dev, "%s: abort successful\n", __func__);
> +             break;
> +
> +     default: /* bad control flag */
> +             dev_warn(dev, "%s: unknown control flag: %#x\n",
> +                             __func__, nd_cmd->ctrl_flags);
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static int nd_intel_test_finish_query(struct nfit_test *t,
> +             struct nd_intel_fw_finish_query *nd_cmd,
> +             unsigned int buf_len, int idx)
> +{
> +     struct device *dev = &t->pdev.dev;
> +     struct nfit_test_fw *fw = &t->fw[idx];
> +
> +     dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx:
> %d)\n",
> +                     __func__, t, nd_cmd, buf_len, idx);
> +
> +     if (buf_len < sizeof(*nd_cmd))
> +             return -EINVAL;
> +
> +     if (nd_cmd->context != fw->context) {
> +             dev_dbg(dev, "%s: incorrect context: in: %#x
> correct: %#x\n",
> +                             __func__, nd_cmd->context, fw-
> >context);
> +             nd_cmd->status = 0x10007;
> +             return 0;
> +     }
> +
> +     dev_dbg(dev, "%s context: %#x\n", __func__, nd_cmd-
> >context);
> +
> +     switch (fw->state) {
> +     case FW_STATE_NEW:
> +             nd_cmd->updated_fw_rev = 0;
> +             nd_cmd->status = 0;
> +             dev_dbg(dev, "%s: new state\n", __func__);
> +             break;
> +
> +     case FW_STATE_IN_PROGRESS:
> +             /* sequencing error */
> +             nd_cmd->status = 0x40007;
> +             nd_cmd->updated_fw_rev = 0;
> +             dev_dbg(dev, "%s: sequence error\n", __func__);
> +             break;
> +
> +     case FW_STATE_VERIFY:
> +             if (time_is_after_jiffies64(fw->end_time)) {
> +                     nd_cmd->updated_fw_rev = 0;
> +                     nd_cmd->status = 0x20007;
> +                     dev_dbg(dev, "%s: still verifying\n",
> __func__);
> +                     break;
> +             }
> +
> +             dev_dbg(dev, "%s: transition out verify\n",
> __func__);
> +             fw->state = FW_STATE_UPDATED;
> +             /* we are going to fall through if it's "done" */
> +     case FW_STATE_UPDATED:
> +             nd_cmd->status = 0;
> +             /* bogus test version */
> +             fw->version = nd_cmd->updated_fw_rev =
> +                     INTEL_FW_FAKE_VERSION;
> +             dev_dbg(dev, "%s: updated\n", __func__);
> +             break;
> +
> +     default: /* we should never get here */
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
>  static int nfit_test_cmd_get_config_size(struct
> nd_cmd_get_config_size *nd_cmd,
>               unsigned int buf_len)
>  {
> @@ -592,6 +821,23 @@ static int
> nfit_test_cmd_ars_inject_status(struct nfit_test *t,
>       return 0;
>  }
>  
> +static int get_dimm(struct nfit_mem *nfit_mem, unsigned int func)
> +{
> +     int i;
> +
> +     /* lookup per-dimm data */
> +     for (i = 0; i < ARRAY_SIZE(handle); i++)
> +             if (__to_nfit_memdev(nfit_mem)->device_handle ==
> handle[i])
> +                     break;
> +     if (i >= ARRAY_SIZE(handle))
> +             return -ENXIO;
> +
> +     if ((1 << func) & dimm_fail_cmd_flags[i])
> +             return -EIO;
> +
> +     return i;
> +}
> +
>  static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
>               struct nvdimm *nvdimm, unsigned int cmd, void *buf,
>               unsigned int buf_len, int *cmd_rc)
> @@ -620,22 +866,39 @@ static int nfit_test_ctl(struct
> nvdimm_bus_descriptor *nd_desc,
>                       func = call_pkg->nd_command;
>                       if (call_pkg->nd_family != nfit_mem->family)
>                               return -ENOTTY;
> +
> +                     i = get_dimm(nfit_mem, func);
> +                     if (i < 0)
> +                             return i;
> +
> +                     switch (func) {
> +                     case ND_INTEL_FW_GET_INFO:
> +                             return nd_intel_test_get_fw_info(t,
> buf,
> +                                             buf_len, i - t-
> >dcr_idx);
> +                     case ND_INTEL_FW_START_UPDATE:
> +                             return nd_intel_test_start_update(t,
> buf,
> +                                             buf_len, i - t-
> >dcr_idx);
> +                     case ND_INTEL_FW_SEND_DATA:
> +                             return nd_intel_test_send_data(t,
> buf,
> +                                             buf_len, i - t-
> >dcr_idx);
> +                     case ND_INTEL_FW_FINISH_UPDATE:
> +                             return nd_intel_test_finish_fw(t,
> buf,
> +                                             buf_len, i - t-
> >dcr_idx);
> +                     case ND_INTEL_FW_FINISH_QUERY:
> +                             return nd_intel_test_finish_query(t,
> buf,
> +                                             buf_len, i - t-
> >dcr_idx);
> +                     default:
> +                             return -ENOTTY;
> +                     }
>               }
>  
>               if (!test_bit(cmd, &cmd_mask)
>                               || !test_bit(func, &nfit_mem-
> >dsm_mask))
>                       return -ENOTTY;
>  
> -             /* lookup per-dimm data */
> -             for (i = 0; i < ARRAY_SIZE(handle); i++)
> -                     if (__to_nfit_memdev(nfit_mem)-
> >device_handle ==
> -                                     handle[i])
> -                             break;
> -             if (i >= ARRAY_SIZE(handle))
> -                     return -ENXIO;
> -
> -             if ((1 << func) & dimm_fail_cmd_flags[i])
> -                     return -EIO;
> +             i = get_dimm(nfit_mem, func);
> +             if (i < 0)
> +                     return i;
>  
>               switch (func) {
>               case ND_CMD_GET_CONFIG_SIZE:
> @@ -1728,6 +1991,11 @@ static void nfit_test0_setup(struct nfit_test
> *t)
>       set_bit(NFIT_CMD_ARS_INJECT_SET, &acpi_desc-
> >bus_nfit_cmd_force_en);
>       set_bit(NFIT_CMD_ARS_INJECT_CLEAR, &acpi_desc-
> >bus_nfit_cmd_force_en);
>       set_bit(NFIT_CMD_ARS_INJECT_GET, &acpi_desc-
> >bus_nfit_cmd_force_en);
> +     set_bit(ND_INTEL_FW_GET_INFO, &acpi_desc-
> >dimm_cmd_force_en);
> +     set_bit(ND_INTEL_FW_START_UPDATE, &acpi_desc-
> >dimm_cmd_force_en);
> +     set_bit(ND_INTEL_FW_SEND_DATA, &acpi_desc-
> >dimm_cmd_force_en);
> +     set_bit(ND_INTEL_FW_FINISH_UPDATE, &acpi_desc-
> >dimm_cmd_force_en);
> +     set_bit(ND_INTEL_FW_FINISH_QUERY, &acpi_desc-
> >dimm_cmd_force_en);
>  }
>  
>  static void nfit_test1_setup(struct nfit_test *t)
> @@ -2134,10 +2402,13 @@ static int nfit_test_probe(struct
> platform_device *pdev)
>               nfit_test->smart_threshold = devm_kcalloc(dev, num,
>                               sizeof(struct
> nd_intel_smart_threshold),
>                               GFP_KERNEL);
> +             nfit_test->fw = devm_kcalloc(dev, num,
> +                             sizeof(struct nfit_test_fw),
> GFP_KERNEL);
>               if (nfit_test->dimm && nfit_test->dimm_dma &&
> nfit_test->label
>                               && nfit_test->label_dma &&
> nfit_test->dcr
>                               && nfit_test->dcr_dma && nfit_test-
> >flush
> -                             && nfit_test->flush_dma)
> +                             && nfit_test->flush_dma
> +                             && nfit_test->fw)
>                       /* pass */;
>               else
>                       return -ENOMEM;
> diff --git a/tools/testing/nvdimm/test/nfit_test.h
> b/tools/testing/nvdimm/test/nfit_test.h
> index ba230f6f7676..be8fa8ec0615 100644
> --- a/tools/testing/nvdimm/test/nfit_test.h
> +++ b/tools/testing/nvdimm/test/nfit_test.h
> @@ -84,9 +84,14 @@ struct nd_cmd_ars_err_inj_stat {
>       } __packed record[0];
>  } __packed;
>  
> -#define ND_INTEL_SMART 1
> -#define ND_INTEL_SMART_THRESHOLD 2
> -#define ND_INTEL_SMART_SET_THRESHOLD 17
> +#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_QUERY     16
> +#define ND_INTEL_SMART_SET_THRESHOLD 17
>  
>  #define ND_INTEL_SMART_HEALTH_VALID             (1 << 0)
>  #define ND_INTEL_SMART_SPARES_VALID             (1 << 1)
> @@ -152,6 +157,61 @@ struct nd_intel_smart_set_threshold {
>       __u32 status;
>  } __packed;
>  
> +#define INTEL_FW_STORAGE_SIZE                0x100000
> +#define INTEL_FW_MAX_SEND_LEN                0xFFEC
> +#define INTEL_FW_QUERY_INTERVAL              250000
> +#define INTEL_FW_QUERY_MAX_TIME              3000000
> +#define INTEL_FW_FIS_VERSION         0x0105
> +#define INTEL_FW_FAKE_VERSION                0xffffffffabcd
> +
> +enum intel_fw_update_state {
> +     FW_STATE_NEW = 0,
> +     FW_STATE_IN_PROGRESS,
> +     FW_STATE_VERIFY,
> +     FW_STATE_UPDATED,
> +};
> +
> +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;
> +} __packed;
> +
> +struct nd_intel_fw_start {
> +     __u32 status;
> +     __u32 context;
> +} __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];
> +/* this field is not declared due ot variable data from input */
> +/*   __u32 status; */
> +} __packed;
> +
> +struct nd_intel_fw_finish_update {
> +     __u8 ctrl_flags;
> +     __u8 reserved[3];
> +     __u32 context;
> +     __u32 status;
> +} __packed;
> +
> +struct nd_intel_fw_finish_query {
> +     __u32 context;
> +     __u32 status;
> +     __u64 updated_fw_rev;
> +} __packed;
> +
>  union acpi_object;
>  typedef void *acpi_handle;
>  
> 
> _______________________________________________
> Linux-nvdimm mailing list
> [email protected]
> https://lists.01.org/mailman/listinfo/linux-nvdimm
_______________________________________________
Linux-nvdimm mailing list
[email protected]
https://lists.01.org/mailman/listinfo/linux-nvdimm

Reply via email to