Merge branch 'for-4.7/dsm' into libnvdimm-for-next
authorDan Williams <dan.j.williams@intel.com>
Wed, 18 May 2016 17:06:59 +0000 (10:06 -0700)
committerDan Williams <dan.j.williams@intel.com>
Wed, 18 May 2016 17:06:59 +0000 (10:06 -0700)
drivers/acpi/nfit.c
drivers/acpi/nfit.h
drivers/acpi/utils.c
drivers/nvdimm/bus.c
drivers/nvdimm/core.c
drivers/nvdimm/dimm_devs.c
drivers/nvdimm/nd-core.h
include/acpi/acpi_bus.h
include/linux/libnvdimm.h
include/uapi/linux/ndctl.h
tools/testing/nvdimm/test/nfit.c

index d60f73a63c3cdc4579993267357ba996f23c0b1d..2564f330a93e44c1eff3a7435572df78c639a1a7 100644 (file)
@@ -45,6 +45,11 @@ module_param(scrub_overflow_abort, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(scrub_overflow_abort,
                "Number of times we overflow ARS results before abort");
 
+static bool disable_vendor_specific;
+module_param(disable_vendor_specific, bool, S_IRUGO);
+MODULE_PARM_DESC(disable_vendor_specific,
+               "Limit commands to the publicly specified set\n");
+
 static struct workqueue_struct *nfit_wq;
 
 struct nfit_table_prev {
@@ -171,33 +176,46 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
                unsigned int buf_len, int *cmd_rc)
 {
        struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
-       const struct nd_cmd_desc *desc = NULL;
        union acpi_object in_obj, in_buf, *out_obj;
+       const struct nd_cmd_desc *desc = NULL;
        struct device *dev = acpi_desc->dev;
+       struct nd_cmd_pkg *call_pkg = NULL;
        const char *cmd_name, *dimm_name;
-       unsigned long dsm_mask;
+       unsigned long cmd_mask, dsm_mask;
        acpi_handle handle;
+       unsigned int func;
        const u8 *uuid;
        u32 offset;
        int rc, i;
 
+       func = cmd;
+       if (cmd == ND_CMD_CALL) {
+               call_pkg = buf;
+               func = call_pkg->nd_command;
+       }
+
        if (nvdimm) {
                struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
                struct acpi_device *adev = nfit_mem->adev;
 
                if (!adev)
                        return -ENOTTY;
+               if (call_pkg && nfit_mem->family != call_pkg->nd_family)
+                       return -ENOTTY;
+
                dimm_name = nvdimm_name(nvdimm);
                cmd_name = nvdimm_cmd_name(cmd);
+               cmd_mask = nvdimm_cmd_mask(nvdimm);
                dsm_mask = nfit_mem->dsm_mask;
                desc = nd_cmd_dimm_desc(cmd);
-               uuid = to_nfit_uuid(NFIT_DEV_DIMM);
+               uuid = to_nfit_uuid(nfit_mem->family);
                handle = adev->handle;
        } else {
                struct acpi_device *adev = to_acpi_dev(acpi_desc);
 
                cmd_name = nvdimm_bus_cmd_name(cmd);
-               dsm_mask = nd_desc->dsm_mask;
+               cmd_mask = nd_desc->cmd_mask;
+               dsm_mask = cmd_mask;
                desc = nd_cmd_bus_desc(cmd);
                uuid = to_nfit_uuid(NFIT_DEV_BUS);
                handle = adev->handle;
@@ -207,7 +225,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
        if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
                return -ENOTTY;
 
-       if (!test_bit(cmd, &dsm_mask))
+       if (!test_bit(cmd, &cmd_mask) || !test_bit(func, &dsm_mask))
                return -ENOTTY;
 
        in_obj.type = ACPI_TYPE_PACKAGE;
@@ -222,21 +240,44 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
                in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
                                i, buf);
 
+       if (call_pkg) {
+               /* skip over package wrapper */
+               in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
+               in_buf.buffer.length = call_pkg->nd_size_in;
+       }
+
        if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) {
-               dev_dbg(dev, "%s:%s cmd: %s input length: %d\n", __func__,
-                               dimm_name, cmd_name, in_buf.buffer.length);
-               print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4,
-                               4, in_buf.buffer.pointer, min_t(u32, 128,
-                                       in_buf.buffer.length), true);
+               dev_dbg(dev, "%s:%s cmd: %d: func: %d input length: %d\n",
+                               __func__, dimm_name, cmd, func,
+                               in_buf.buffer.length);
+               print_hex_dump_debug("nvdimm in  ", DUMP_PREFIX_OFFSET, 4, 4,
+                       in_buf.buffer.pointer,
+                       min_t(u32, 256, in_buf.buffer.length), true);
        }
 
-       out_obj = acpi_evaluate_dsm(handle, uuid, 1, cmd, &in_obj);
+       out_obj = acpi_evaluate_dsm(handle, uuid, 1, func, &in_obj);
        if (!out_obj) {
                dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
                                cmd_name);
                return -EINVAL;
        }
 
+       if (call_pkg) {
+               call_pkg->nd_fw_size = out_obj->buffer.length;
+               memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
+                       out_obj->buffer.pointer,
+                       min(call_pkg->nd_fw_size, call_pkg->nd_size_out));
+
+               ACPI_FREE(out_obj);
+               /*
+                * Need to support FW function w/o known size in advance.
+                * Caller can determine required size based upon nd_fw_size.
+                * If we return an error (like elsewhere) then caller wouldn't
+                * be able to rely upon data returned to make calculation.
+                */
+               return 0;
+       }
+
        if (out_obj->package.type != ACPI_TYPE_BUFFER) {
                dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n",
                                __func__, dimm_name, cmd_name, out_obj->type);
@@ -921,6 +962,30 @@ static ssize_t serial_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(serial);
 
+static ssize_t family_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvdimm *nvdimm = to_nvdimm(dev);
+       struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+       if (nfit_mem->family < 0)
+               return -ENXIO;
+       return sprintf(buf, "%d\n", nfit_mem->family);
+}
+static DEVICE_ATTR_RO(family);
+
+static ssize_t dsm_mask_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvdimm *nvdimm = to_nvdimm(dev);
+       struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+
+       if (nfit_mem->family < 0)
+               return -ENXIO;
+       return sprintf(buf, "%#lx\n", nfit_mem->dsm_mask);
+}
+static DEVICE_ATTR_RO(dsm_mask);
+
 static ssize_t flags_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -946,6 +1011,8 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
        &dev_attr_serial.attr,
        &dev_attr_rev_id.attr,
        &dev_attr_flags.attr,
+       &dev_attr_family.attr,
+       &dev_attr_dsm_mask.attr,
        NULL,
 };
 
@@ -992,10 +1059,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
 {
        struct acpi_device *adev, *adev_dimm;
        struct device *dev = acpi_desc->dev;
-       const u8 *uuid = to_nfit_uuid(NFIT_DEV_DIMM);
+       unsigned long dsm_mask;
+       const u8 *uuid;
        int i;
 
-       nfit_mem->dsm_mask = acpi_desc->dimm_dsm_force_en;
+       /* nfit test assumes 1:1 relationship between commands and dsms */
+       nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en;
+       nfit_mem->family = NVDIMM_FAMILY_INTEL;
        adev = to_acpi_dev(acpi_desc);
        if (!adev)
                return 0;
@@ -1008,7 +1078,35 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
                return force_enable_dimms ? 0 : -ENODEV;
        }
 
-       for (i = ND_CMD_SMART; i <= ND_CMD_VENDOR; i++)
+       /*
+        * Until standardization materializes we need to consider up to 3
+        * different command sets.  Note, that checking for function0 (bit0)
+        * tells us if any commands are reachable through this uuid.
+        */
+       for (i = NVDIMM_FAMILY_INTEL; i <= NVDIMM_FAMILY_HPE2; i++)
+               if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1))
+                       break;
+
+       /* limit the supported commands to those that are publicly documented */
+       nfit_mem->family = i;
+       if (nfit_mem->family == NVDIMM_FAMILY_INTEL) {
+               dsm_mask = 0x3fe;
+               if (disable_vendor_specific)
+                       dsm_mask &= ~(1 << ND_CMD_VENDOR);
+       } else if (nfit_mem->family == NVDIMM_FAMILY_HPE1)
+               dsm_mask = 0x1c3c76;
+       else if (nfit_mem->family == NVDIMM_FAMILY_HPE2) {
+               dsm_mask = 0x1fe;
+               if (disable_vendor_specific)
+                       dsm_mask &= ~(1 << 8);
+       } else {
+               dev_err(dev, "unknown dimm command family\n");
+               nfit_mem->family = -1;
+               return force_enable_dimms ? 0 : -ENODEV;
+       }
+
+       uuid = to_nfit_uuid(nfit_mem->family);
+       for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
                if (acpi_check_dsm(adev_dimm->handle, uuid, 1, 1ULL << i))
                        set_bit(i, &nfit_mem->dsm_mask);
 
@@ -1021,8 +1119,8 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
        int dimm_count = 0;
 
        list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+               unsigned long flags = 0, cmd_mask;
                struct nvdimm *nvdimm;
-               unsigned long flags = 0;
                u32 device_handle;
                u16 mem_flags;
                int rc;
@@ -1045,9 +1143,18 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
                if (rc)
                        continue;
 
+               /*
+                * TODO: provide translation for non-NVDIMM_FAMILY_INTEL
+                * devices (i.e. from nd_cmd to acpi_dsm) to standardize the
+                * userspace interface.
+                */
+               cmd_mask = 1UL << ND_CMD_CALL;
+               if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
+                       cmd_mask |= nfit_mem->dsm_mask;
+
                nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
                                acpi_nfit_dimm_attribute_groups,
-                               flags, &nfit_mem->dsm_mask);
+                               flags, cmd_mask);
                if (!nvdimm)
                        return -ENOMEM;
 
@@ -1076,14 +1183,14 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
        struct acpi_device *adev;
        int i;
 
-       nd_desc->dsm_mask = acpi_desc->bus_dsm_force_en;
+       nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en;
        adev = to_acpi_dev(acpi_desc);
        if (!adev)
                return;
 
        for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++)
                if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i))
-                       set_bit(i, &nd_desc->dsm_mask);
+                       set_bit(i, &nd_desc->cmd_mask);
 }
 
 static ssize_t range_index_show(struct device *dev,
@@ -2532,6 +2639,8 @@ static __init int nfit_init(void)
        acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_CD, nfit_uuid[NFIT_SPA_PCD]);
        acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]);
        acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]);
+       acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE1, nfit_uuid[NFIT_DEV_DIMM_N_HPE1]);
+       acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE2, nfit_uuid[NFIT_DEV_DIMM_N_HPE2]);
 
        nfit_wq = create_singlethread_workqueue("nfit");
        if (!nfit_wq)
index 5201840c1147e496c77243325870992cd074411a..46c50148e4b79cf8d05999a3ce651564b805e537 100644 (file)
 #include <linux/acpi.h>
 #include <acpi/acuuid.h>
 
+/* ACPI 6.1 */
 #define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba"
+
+/* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf */
 #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66"
+
+/* https://github.com/HewlettPackard/hpe-nvm/blob/master/Documentation/ */
+#define UUID_NFIT_DIMM_N_HPE1 "9002c334-acf3-4c0e-9642-a235f0d53bc6"
+#define UUID_NFIT_DIMM_N_HPE2 "5008664b-b758-41a0-a03c-27c2f2d04f7e"
+
 #define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \
                | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
                | ACPI_NFIT_MEM_NOT_ARMED)
 
 enum nfit_uuids {
+       /* for simplicity alias the uuid index with the family id */
+       NFIT_DEV_DIMM = NVDIMM_FAMILY_INTEL,
+       NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1,
+       NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2,
        NFIT_SPA_VOLATILE,
        NFIT_SPA_PM,
        NFIT_SPA_DCR,
@@ -37,7 +49,6 @@ enum nfit_uuids {
        NFIT_SPA_PDISK,
        NFIT_SPA_PCD,
        NFIT_DEV_BUS,
-       NFIT_DEV_DIMM,
        NFIT_UUID_MAX,
 };
 
@@ -111,6 +122,7 @@ struct nfit_mem {
        struct acpi_device *adev;
        struct acpi_nfit_desc *acpi_desc;
        unsigned long dsm_mask;
+       int family;
 };
 
 struct acpi_nfit_desc {
@@ -133,8 +145,8 @@ struct acpi_nfit_desc {
        size_t ars_status_size;
        struct work_struct work;
        unsigned int cancel:1;
-       unsigned long dimm_dsm_force_en;
-       unsigned long bus_dsm_force_en;
+       unsigned long dimm_cmd_force_en;
+       unsigned long bus_cmd_force_en;
        int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
                        void *iobuf, u64 len, int rw);
 };
index 050673f0c0b3e4e382ad223e03ac4d655eb1be60..e854dea7d5fe392ed1c8043084ab8f00a0bd7a85 100644 (file)
@@ -625,7 +625,7 @@ acpi_status acpi_evaluate_lck(acpi_handle handle, int lock)
  * some old BIOSes do expect a buffer or an integer etc.
  */
 union acpi_object *
-acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, int rev, int func,
+acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 func,
                  union acpi_object *argv4)
 {
        acpi_status ret;
@@ -674,7 +674,7 @@ EXPORT_SYMBOL(acpi_evaluate_dsm);
  * functions. Currently only support 64 functions at maximum, should be
  * enough for now.
  */
-bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs)
+bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs)
 {
        int i;
        u64 mask = 0;
index dcaefe229887a6ccd241cbe7620a81574075dcfb..04c2c3fda1abd9ce7c060f070e0cac85f07da7c5 100644 (file)
@@ -443,6 +443,12 @@ static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = {
                .out_num = 3,
                .out_sizes = { 4, 4, UINT_MAX, },
        },
+       [ND_CMD_CALL] = {
+               .in_num = 2,
+               .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
+               .out_num = 1,
+               .out_sizes = { UINT_MAX, },
+       },
 };
 
 const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd)
@@ -477,6 +483,12 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
                .out_num = 3,
                .out_sizes = { 4, 4, 8, },
        },
+       [ND_CMD_CALL] = {
+               .in_num = 2,
+               .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
+               .out_num = 1,
+               .out_sizes = { UINT_MAX, },
+       },
 };
 
 const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd)
@@ -504,6 +516,10 @@ u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
                struct nd_cmd_vendor_hdr *hdr = buf;
 
                return hdr->in_length;
+       } else if (cmd == ND_CMD_CALL) {
+               struct nd_cmd_pkg *pkg = buf;
+
+               return pkg->nd_size_in;
        }
 
        return UINT_MAX;
@@ -526,6 +542,12 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
                return out_field[1];
        else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2)
                return out_field[1] - 8;
+       else if (cmd == ND_CMD_CALL) {
+               struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field;
+
+               return pkg->nd_size_out;
+       }
+
 
        return UINT_MAX;
 }
@@ -592,25 +614,31 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
        unsigned int cmd = _IOC_NR(ioctl_cmd);
        void __user *p = (void __user *) arg;
        struct device *dev = &nvdimm_bus->dev;
+       struct nd_cmd_pkg pkg;
        const char *cmd_name, *dimm_name;
-       unsigned long dsm_mask;
+       unsigned long cmd_mask;
        void *buf;
        int rc, i;
 
        if (nvdimm) {
                desc = nd_cmd_dimm_desc(cmd);
                cmd_name = nvdimm_cmd_name(cmd);
-               dsm_mask = nvdimm->dsm_mask ? *(nvdimm->dsm_mask) : 0;
+               cmd_mask = nvdimm->cmd_mask;
                dimm_name = dev_name(&nvdimm->dev);
        } else {
                desc = nd_cmd_bus_desc(cmd);
                cmd_name = nvdimm_bus_cmd_name(cmd);
-               dsm_mask = nd_desc->dsm_mask;
+               cmd_mask = nd_desc->cmd_mask;
                dimm_name = "bus";
        }
 
+       if (cmd == ND_CMD_CALL) {
+               if (copy_from_user(&pkg, p, sizeof(pkg)))
+                       return -EFAULT;
+       }
+
        if (!desc || (desc->out_num + desc->in_num == 0) ||
-                       !test_bit(cmd, &dsm_mask))
+                       !test_bit(cmd, &cmd_mask))
                return -ENOTTY;
 
        /* fail write commands (when read-only) */
@@ -620,6 +648,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
                case ND_CMD_SET_CONFIG_DATA:
                case ND_CMD_ARS_START:
                case ND_CMD_CLEAR_ERROR:
+               case ND_CMD_CALL:
                        dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n",
                                        nvdimm ? nvdimm_cmd_name(cmd)
                                        : nvdimm_bus_cmd_name(cmd));
@@ -647,6 +676,16 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
                in_len += in_size;
        }
 
+       if (cmd == ND_CMD_CALL) {
+               dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n",
+                               __func__, dimm_name, pkg.nd_command,
+                               in_len, out_len, buf_len);
+
+               for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++)
+                       if (pkg.nd_reserved2[i])
+                               return -EINVAL;
+       }
+
        /* process an output envelope */
        for (i = 0; i < desc->out_num; i++) {
                u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i,
index 182a93fe37128d87d53b3c77d121ca28bb3ec8fd..e8688a13cf4fa15c8356ec5a77b1716ed2cc13b0 100644 (file)
@@ -251,7 +251,7 @@ static ssize_t commands_show(struct device *dev,
        struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
        struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
 
-       for_each_set_bit(cmd, &nd_desc->dsm_mask, BITS_PER_LONG)
+       for_each_set_bit(cmd, &nd_desc->cmd_mask, BITS_PER_LONG)
                len += sprintf(buf + len, "%s ", nvdimm_bus_cmd_name(cmd));
        len += sprintf(buf + len, "\n");
        return len;
index c56f88217924b93b3dca5f594c01c5edbe801bed..79a35a02053c5638406f688e96a9b3690ea8f7a0 100644 (file)
@@ -37,9 +37,9 @@ static int __validate_dimm(struct nvdimm_drvdata *ndd)
 
        nvdimm = to_nvdimm(ndd->dev);
 
-       if (!nvdimm->dsm_mask)
+       if (!nvdimm->cmd_mask)
                return -ENXIO;
-       if (!test_bit(ND_CMD_GET_CONFIG_DATA, nvdimm->dsm_mask))
+       if (!test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask))
                return -ENXIO;
 
        return 0;
@@ -263,6 +263,12 @@ const char *nvdimm_name(struct nvdimm *nvdimm)
 }
 EXPORT_SYMBOL_GPL(nvdimm_name);
 
+unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
+{
+       return nvdimm->cmd_mask;
+}
+EXPORT_SYMBOL_GPL(nvdimm_cmd_mask);
+
 void *nvdimm_provider_data(struct nvdimm *nvdimm)
 {
        if (nvdimm)
@@ -277,10 +283,10 @@ static ssize_t commands_show(struct device *dev,
        struct nvdimm *nvdimm = to_nvdimm(dev);
        int cmd, len = 0;
 
-       if (!nvdimm->dsm_mask)
+       if (!nvdimm->cmd_mask)
                return sprintf(buf, "\n");
 
-       for_each_set_bit(cmd, nvdimm->dsm_mask, BITS_PER_LONG)
+       for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG)
                len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd));
        len += sprintf(buf + len, "\n");
        return len;
@@ -340,7 +346,7 @@ EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
 
 struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
                const struct attribute_group **groups, unsigned long flags,
-               unsigned long *dsm_mask)
+               unsigned long cmd_mask)
 {
        struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
        struct device *dev;
@@ -355,7 +361,7 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
        }
        nvdimm->provider_data = provider_data;
        nvdimm->flags = flags;
-       nvdimm->dsm_mask = dsm_mask;
+       nvdimm->cmd_mask = cmd_mask;
        atomic_set(&nvdimm->busy, 0);
        dev = &nvdimm->dev;
        dev_set_name(dev, "nmem%d", nvdimm->id);
index cb65308c032918d40b6aefbe3766ed5223586cc0..86d985ccce82a8ff72312b9b4738e01b36158f90 100644 (file)
@@ -37,7 +37,7 @@ struct nvdimm_bus {
 struct nvdimm {
        unsigned long flags;
        void *provider_data;
-       unsigned long *dsm_mask;
+       unsigned long cmd_mask;
        struct device dev;
        atomic_t busy;
        int id;
index 14362a84c78e3ca1d5712d284d6dce22ed656ba1..f092cc6eb1fbe1b86ca765c8e8fb72a01cfa6b95 100644 (file)
@@ -61,12 +61,12 @@ bool acpi_ata_match(acpi_handle handle);
 bool acpi_bay_match(acpi_handle handle);
 bool acpi_dock_match(acpi_handle handle);
 
-bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs);
+bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs);
 union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid,
-                       int rev, int func, union acpi_object *argv4);
+                       u64 rev, u64 func, union acpi_object *argv4);
 
 static inline union acpi_object *
-acpi_evaluate_dsm_typed(acpi_handle handle, const u8 *uuid, int rev, int func,
+acpi_evaluate_dsm_typed(acpi_handle handle, const u8 *uuid, u64 rev, u64 func,
                        union acpi_object *argv4, acpi_object_type type)
 {
        union acpi_object *obj;
index 833867b9ddc28a702834874072441403518133d5..0c3c30cbbea54431cff38dc01f21ba3d831674d4 100644 (file)
@@ -27,7 +27,7 @@ enum {
        /* need to set a limit somewhere, but yes, this is likely overkill */
        ND_IOCTL_MAX_BUFLEN = SZ_4M,
        ND_CMD_MAX_ELEM = 5,
-       ND_CMD_MAX_ENVELOPE = 16,
+       ND_CMD_MAX_ENVELOPE = 256,
        ND_MAX_MAPPINGS = 32,
 
        /* region flag indicating to direct-map persistent memory by default */
@@ -68,7 +68,7 @@ struct nd_mapping {
 
 struct nvdimm_bus_descriptor {
        const struct attribute_group **attr_groups;
-       unsigned long dsm_mask;
+       unsigned long cmd_mask;
        char *provider_name;
        ndctl_fn ndctl;
        int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc);
@@ -130,10 +130,11 @@ struct nd_region *to_nd_region(struct device *dev);
 struct nd_blk_region *to_nd_blk_region(struct device *dev);
 struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus);
 const char *nvdimm_name(struct nvdimm *nvdimm);
+unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm);
 void *nvdimm_provider_data(struct nvdimm *nvdimm);
 struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
                const struct attribute_group **groups, unsigned long flags,
-               unsigned long *dsm_mask);
+               unsigned long cmd_mask);
 const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd);
 const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd);
 u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
index 1eac426aead54231a313ed213f6096b01cc1d672..309915f74492406595782af8e8c4e20a5a7ef934 100644 (file)
@@ -159,6 +159,7 @@ enum {
        ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7,
        ND_CMD_VENDOR_EFFECT_LOG = 8,
        ND_CMD_VENDOR = 9,
+       ND_CMD_CALL = 10,
 };
 
 enum {
@@ -192,6 +193,7 @@ static inline const char *nvdimm_cmd_name(unsigned cmd)
                [ND_CMD_VENDOR_EFFECT_LOG_SIZE] = "effect_size",
                [ND_CMD_VENDOR_EFFECT_LOG] = "effect_log",
                [ND_CMD_VENDOR] = "vendor",
+               [ND_CMD_CALL] = "cmd_call",
        };
 
        if (cmd < ARRAY_SIZE(names) && names[cmd])
@@ -260,4 +262,44 @@ enum ars_masks {
        ARS_STATUS_MASK = 0x0000FFFF,
        ARS_EXT_STATUS_SHIFT = 16,
 };
+
+/*
+ * struct nd_cmd_pkg
+ *
+ * is a wrapper to a quasi pass thru interface for invoking firmware
+ * associated with nvdimms.
+ *
+ * INPUT PARAMETERS
+ *
+ * nd_family corresponds to the firmware (e.g. DSM) interface.
+ *
+ * nd_command are the function index advertised by the firmware.
+ *
+ * nd_size_in is the size of the input parameters being passed to firmware
+ *
+ * OUTPUT PARAMETERS
+ *
+ * nd_fw_size is the size of the data firmware wants to return for
+ * the call.  If nd_fw_size is greater than size of nd_size_out, only
+ * the first nd_size_out bytes are returned.
+ */
+
+struct nd_cmd_pkg {
+       __u64   nd_family;              /* family of commands */
+       __u64   nd_command;
+       __u32   nd_size_in;             /* INPUT: size of input args */
+       __u32   nd_size_out;            /* INPUT: size of payload */
+       __u32   nd_reserved2[9];        /* reserved must be zero */
+       __u32   nd_fw_size;             /* OUTPUT: size fw wants to return */
+       unsigned char nd_payload[];     /* Contents of call      */
+};
+
+/* These NVDIMM families represent pre-standardization command sets */
+#define NVDIMM_FAMILY_INTEL 0
+#define NVDIMM_FAMILY_HPE1 1
+#define NVDIMM_FAMILY_HPE2 2
+
+#define ND_IOCTL_CALL                  _IOWR(ND_IOCTL, ND_CMD_CALL,\
+                                       struct nd_cmd_pkg)
+
 #endif /* __NDCTL_H__ */
index d1c98d4386d4b9713604e8666955159af28bb62b..c919866853a045fdeb1d7b2c0aae699cddd68200 100644 (file)
@@ -372,6 +372,7 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
 {
        struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
        struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
+       unsigned int func = cmd;
        int i, rc = 0, __cmd_rc;
 
        if (!cmd_rc)
@@ -380,8 +381,23 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
 
        if (nvdimm) {
                struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
+               unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
 
-               if (!nfit_mem || !test_bit(cmd, &nfit_mem->dsm_mask))
+               if (!nfit_mem)
+                       return -ENOTTY;
+
+               if (cmd == ND_CMD_CALL) {
+                       struct nd_cmd_pkg *call_pkg = buf;
+
+                       buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
+                       buf = (void *) call_pkg->nd_payload;
+                       func = call_pkg->nd_command;
+                       if (call_pkg->nd_family != nfit_mem->family)
+                               return -ENOTTY;
+               }
+
+               if (!test_bit(cmd, &cmd_mask)
+                               || !test_bit(func, &nfit_mem->dsm_mask))
                        return -ENOTTY;
 
                /* lookup label space for the given dimm */
@@ -392,7 +408,7 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
                if (i >= ARRAY_SIZE(handle))
                        return -ENXIO;
 
-               switch (cmd) {
+               switch (func) {
                case ND_CMD_GET_CONFIG_SIZE:
                        rc = nfit_test_cmd_get_config_size(buf, buf_len);
                        break;
@@ -416,10 +432,10 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
        } else {
                struct ars_state *ars_state = &t->ars_state;
 
-               if (!nd_desc || !test_bit(cmd, &nd_desc->dsm_mask))
+               if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask))
                        return -ENOTTY;
 
-               switch (cmd) {
+               switch (func) {
                case ND_CMD_ARS_CAP:
                        rc = nfit_test_cmd_ars_cap(buf, buf_len);
                        break;
@@ -1293,15 +1309,15 @@ static void nfit_test0_setup(struct nfit_test *t)
        post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA0_SIZE);
 
        acpi_desc = &t->acpi_desc;
-       set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en);
-       set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
-       set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
-       set_bit(ND_CMD_SMART, &acpi_desc->dimm_dsm_force_en);
-       set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en);
-       set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en);
-       set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
-       set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en);
-       set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_dsm_force_en);
+       set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en);
+       set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
+       set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
+       set_bit(ND_CMD_SMART, &acpi_desc->dimm_cmd_force_en);
+       set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
 }
 
 static void nfit_test1_setup(struct nfit_test *t)
@@ -1359,10 +1375,10 @@ static void nfit_test1_setup(struct nfit_test *t)
        post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA2_SIZE);
 
        acpi_desc = &t->acpi_desc;
-       set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en);
-       set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en);
-       set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
-       set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en);
+       set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
 }
 
 static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,