summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-04-28 19:23:43 -0400
committerDan Williams <dan.j.williams@intel.com>2016-04-28 19:59:06 -0400
commit31eca76ba2fc988bf88f16fcf763a0ec4068cd30 (patch)
tree7b7f359ac8340576842350708cd4de5b84bbf53a
parente3654eca70d63704c94a60a2aafc0b3c7b46a00b (diff)
nfit, libnvdimm: limited/whitelisted dimm command marshaling mechanism
There are currently 4 known similar but incompatible definitions of the command sets that can be sent to an NVDIMM through ACPI. It is also clear that future platform generations (ACPI or not) will continue to revise and extend the DIMM command set as new devices and use cases arrive. It is obviously untenable to continue to proliferate divergence of these command definitions, and to that end a standardization process has begun to provide for a unified specification. However, that leaves a problem about what to do with this first generation where vendors are already shipping divergence. The Linux kernel can support these initial diverged platforms without giving platform-firmware free reign to continue to diverge and compound kernel maintenance overhead. The kernel implementation can encourage standardization in two ways: 1/ Require that any function code that userspace wants to send be explicitly white-listed in the implementation. For ACPI this means function codes marked as supported by acpi_check_dsm() may only be invoked if they appear in the white-list. A function must be publicly documented before it is added to the white-list. 2/ The above restrictions can be trivially bypassed by using the "vendor-specific" payload command. However, since vendor-specific commands are by definition not publicly documented and have the potential to corrupt the kernel's view of the dimm state, we provide a toggle to disable vendor-specific operations. Enabling undefined behavior is a policy decision that can be made by the platform owner and encourages firmware implementations to choose public over private command implementations. Based on an initial patch from Jerry Hoemann Cc: Jerry Hoemann <jerry.hoemann@hpe.com> Cc: Christoph Hellwig <hch@infradead.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/acpi/nfit.c101
-rw-r--r--drivers/acpi/nfit.h14
-rw-r--r--drivers/nvdimm/bus.c39
-rw-r--r--include/uapi/linux/ndctl.h42
4 files changed, 179 insertions, 17 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index 1b98e9dc6138..b85a46873228 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -171,33 +171,46 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
171 unsigned int buf_len, int *cmd_rc) 171 unsigned int buf_len, int *cmd_rc)
172{ 172{
173 struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); 173 struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
174 const struct nd_cmd_desc *desc = NULL;
175 union acpi_object in_obj, in_buf, *out_obj; 174 union acpi_object in_obj, in_buf, *out_obj;
175 const struct nd_cmd_desc *desc = NULL;
176 struct device *dev = acpi_desc->dev; 176 struct device *dev = acpi_desc->dev;
177 struct nd_cmd_pkg *call_pkg = NULL;
177 const char *cmd_name, *dimm_name; 178 const char *cmd_name, *dimm_name;
178 unsigned long cmd_mask; 179 unsigned long cmd_mask, dsm_mask;
179 acpi_handle handle; 180 acpi_handle handle;
181 unsigned int func;
180 const u8 *uuid; 182 const u8 *uuid;
181 u32 offset; 183 u32 offset;
182 int rc, i; 184 int rc, i;
183 185
186 func = cmd;
187 if (cmd == ND_CMD_CALL) {
188 call_pkg = buf;
189 func = call_pkg->nd_command;
190 }
191
184 if (nvdimm) { 192 if (nvdimm) {
185 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 193 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
186 struct acpi_device *adev = nfit_mem->adev; 194 struct acpi_device *adev = nfit_mem->adev;
187 195
188 if (!adev) 196 if (!adev)
189 return -ENOTTY; 197 return -ENOTTY;
198 if (call_pkg && nfit_mem->family != call_pkg->nd_family)
199 return -ENOTTY;
200
190 dimm_name = nvdimm_name(nvdimm); 201 dimm_name = nvdimm_name(nvdimm);
191 cmd_name = nvdimm_cmd_name(cmd); 202 cmd_name = nvdimm_cmd_name(cmd);
192 cmd_mask = nvdimm_cmd_mask(nvdimm); 203 cmd_mask = nvdimm_cmd_mask(nvdimm);
204 dsm_mask = nfit_mem->dsm_mask;
193 desc = nd_cmd_dimm_desc(cmd); 205 desc = nd_cmd_dimm_desc(cmd);
194 uuid = to_nfit_uuid(NFIT_DEV_DIMM); 206 uuid = to_nfit_uuid(nfit_mem->family);
195 handle = adev->handle; 207 handle = adev->handle;
196 } else { 208 } else {
197 struct acpi_device *adev = to_acpi_dev(acpi_desc); 209 struct acpi_device *adev = to_acpi_dev(acpi_desc);
198 210
199 cmd_name = nvdimm_bus_cmd_name(cmd); 211 cmd_name = nvdimm_bus_cmd_name(cmd);
200 cmd_mask = nd_desc->cmd_mask; 212 cmd_mask = nd_desc->cmd_mask;
213 dsm_mask = cmd_mask;
201 desc = nd_cmd_bus_desc(cmd); 214 desc = nd_cmd_bus_desc(cmd);
202 uuid = to_nfit_uuid(NFIT_DEV_BUS); 215 uuid = to_nfit_uuid(NFIT_DEV_BUS);
203 handle = adev->handle; 216 handle = adev->handle;
@@ -207,7 +220,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
207 if (!desc || (cmd && (desc->out_num + desc->in_num == 0))) 220 if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
208 return -ENOTTY; 221 return -ENOTTY;
209 222
210 if (!test_bit(cmd, &cmd_mask)) 223 if (!test_bit(cmd, &cmd_mask) || !test_bit(func, &dsm_mask))
211 return -ENOTTY; 224 return -ENOTTY;
212 225
213 in_obj.type = ACPI_TYPE_PACKAGE; 226 in_obj.type = ACPI_TYPE_PACKAGE;
@@ -222,21 +235,44 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
222 in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc, 235 in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
223 i, buf); 236 i, buf);
224 237
238 if (call_pkg) {
239 /* skip over package wrapper */
240 in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
241 in_buf.buffer.length = call_pkg->nd_size_in;
242 }
243
225 if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) { 244 if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) {
226 dev_dbg(dev, "%s:%s cmd: %s input length: %d\n", __func__, 245 dev_dbg(dev, "%s:%s cmd: %d: func: %d input length: %d\n",
227 dimm_name, cmd_name, in_buf.buffer.length); 246 __func__, dimm_name, cmd, func,
228 print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 247 in_buf.buffer.length);
229 4, in_buf.buffer.pointer, min_t(u32, 128, 248 print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4,
230 in_buf.buffer.length), true); 249 in_buf.buffer.pointer,
250 min_t(u32, 256, in_buf.buffer.length), true);
231 } 251 }
232 252
233 out_obj = acpi_evaluate_dsm(handle, uuid, 1, cmd, &in_obj); 253 out_obj = acpi_evaluate_dsm(handle, uuid, 1, func, &in_obj);
234 if (!out_obj) { 254 if (!out_obj) {
235 dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name, 255 dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
236 cmd_name); 256 cmd_name);
237 return -EINVAL; 257 return -EINVAL;
238 } 258 }
239 259
260 if (call_pkg) {
261 call_pkg->nd_fw_size = out_obj->buffer.length;
262 memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
263 out_obj->buffer.pointer,
264 min(call_pkg->nd_fw_size, call_pkg->nd_size_out));
265
266 ACPI_FREE(out_obj);
267 /*
268 * Need to support FW function w/o known size in advance.
269 * Caller can determine required size based upon nd_fw_size.
270 * If we return an error (like elsewhere) then caller wouldn't
271 * be able to rely upon data returned to make calculation.
272 */
273 return 0;
274 }
275
240 if (out_obj->package.type != ACPI_TYPE_BUFFER) { 276 if (out_obj->package.type != ACPI_TYPE_BUFFER) {
241 dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n", 277 dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n",
242 __func__, dimm_name, cmd_name, out_obj->type); 278 __func__, dimm_name, cmd_name, out_obj->type);
@@ -923,11 +959,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
923{ 959{
924 struct acpi_device *adev, *adev_dimm; 960 struct acpi_device *adev, *adev_dimm;
925 struct device *dev = acpi_desc->dev; 961 struct device *dev = acpi_desc->dev;
926 const u8 *uuid = to_nfit_uuid(NFIT_DEV_DIMM); 962 unsigned long dsm_mask;
963 const u8 *uuid;
927 int i; 964 int i;
928 965
929 /* nfit test assumes 1:1 relationship between commands and dsms */ 966 /* nfit test assumes 1:1 relationship between commands and dsms */
930 nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en; 967 nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en;
968 nfit_mem->family = NVDIMM_FAMILY_INTEL;
931 adev = to_acpi_dev(acpi_desc); 969 adev = to_acpi_dev(acpi_desc);
932 if (!adev) 970 if (!adev)
933 return 0; 971 return 0;
@@ -940,7 +978,31 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
940 return force_enable_dimms ? 0 : -ENODEV; 978 return force_enable_dimms ? 0 : -ENODEV;
941 } 979 }
942 980
943 for (i = ND_CMD_SMART; i <= ND_CMD_VENDOR; i++) 981 /*
982 * Until standardization materializes we need to consider up to 3
983 * different command sets. Note, that checking for function0 (bit0)
984 * tells us if any commands are reachable through this uuid.
985 */
986 for (i = NVDIMM_FAMILY_INTEL; i <= NVDIMM_FAMILY_HPE2; i++)
987 if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1))
988 break;
989
990 /* limit the supported commands to those that are publicly documented */
991 nfit_mem->family = i;
992 if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
993 dsm_mask = 0x3fe;
994 else if (nfit_mem->family == NVDIMM_FAMILY_HPE1)
995 dsm_mask = 0x1c3c76;
996 else if (nfit_mem->family == NVDIMM_FAMILY_HPE2)
997 dsm_mask = 0x1fe;
998 else {
999 dev_err(dev, "unknown dimm command family\n");
1000 nfit_mem->family = -1;
1001 return force_enable_dimms ? 0 : -ENODEV;
1002 }
1003
1004 uuid = to_nfit_uuid(nfit_mem->family);
1005 for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
944 if (acpi_check_dsm(adev_dimm->handle, uuid, 1, 1ULL << i)) 1006 if (acpi_check_dsm(adev_dimm->handle, uuid, 1, 1ULL << i))
945 set_bit(i, &nfit_mem->dsm_mask); 1007 set_bit(i, &nfit_mem->dsm_mask);
946 1008
@@ -953,8 +1015,8 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
953 int dimm_count = 0; 1015 int dimm_count = 0;
954 1016
955 list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { 1017 list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
1018 unsigned long flags = 0, cmd_mask;
956 struct nvdimm *nvdimm; 1019 struct nvdimm *nvdimm;
957 unsigned long flags = 0;
958 u32 device_handle; 1020 u32 device_handle;
959 u16 mem_flags; 1021 u16 mem_flags;
960 int rc; 1022 int rc;
@@ -978,12 +1040,17 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
978 continue; 1040 continue;
979 1041
980 /* 1042 /*
981 * For now there is 1:1 relationship between cmd_mask and 1043 * TODO: provide translation for non-NVDIMM_FAMILY_INTEL
982 * dsm_mask. 1044 * devices (i.e. from nd_cmd to acpi_dsm) to standardize the
1045 * userspace interface.
983 */ 1046 */
1047 cmd_mask = 1UL << ND_CMD_CALL;
1048 if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
1049 cmd_mask |= nfit_mem->dsm_mask;
1050
984 nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, 1051 nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
985 acpi_nfit_dimm_attribute_groups, 1052 acpi_nfit_dimm_attribute_groups,
986 flags, nfit_mem->dsm_mask); 1053 flags, cmd_mask);
987 if (!nvdimm) 1054 if (!nvdimm)
988 return -ENOMEM; 1055 return -ENOMEM;
989 1056
@@ -2468,6 +2535,8 @@ static __init int nfit_init(void)
2468 acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_CD, nfit_uuid[NFIT_SPA_PCD]); 2535 acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_CD, nfit_uuid[NFIT_SPA_PCD]);
2469 acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]); 2536 acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]);
2470 acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]); 2537 acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]);
2538 acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE1, nfit_uuid[NFIT_DEV_DIMM_N_HPE1]);
2539 acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE2, nfit_uuid[NFIT_DEV_DIMM_N_HPE2]);
2471 2540
2472 nfit_wq = create_singlethread_workqueue("nfit"); 2541 nfit_wq = create_singlethread_workqueue("nfit");
2473 if (!nfit_wq) 2542 if (!nfit_wq)
diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h
index 332ee6f01662..f82fda55b6de 100644
--- a/drivers/acpi/nfit.h
+++ b/drivers/acpi/nfit.h
@@ -21,13 +21,25 @@
21#include <linux/acpi.h> 21#include <linux/acpi.h>
22#include <acpi/acuuid.h> 22#include <acpi/acuuid.h>
23 23
24/* ACPI 6.1 */
24#define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba" 25#define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba"
26
27/* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf */
25#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" 28#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66"
29
30/* https://github.com/HewlettPackard/hpe-nvm/blob/master/Documentation/ */
31#define UUID_NFIT_DIMM_N_HPE1 "9002c334-acf3-4c0e-9642-a235f0d53bc6"
32#define UUID_NFIT_DIMM_N_HPE2 "5008664b-b758-41a0-a03c-27c2f2d04f7e"
33
26#define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \ 34#define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \
27 | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ 35 | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
28 | ACPI_NFIT_MEM_NOT_ARMED) 36 | ACPI_NFIT_MEM_NOT_ARMED)
29 37
30enum nfit_uuids { 38enum nfit_uuids {
39 /* for simplicity alias the uuid index with the family id */
40 NFIT_DEV_DIMM = NVDIMM_FAMILY_INTEL,
41 NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1,
42 NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2,
31 NFIT_SPA_VOLATILE, 43 NFIT_SPA_VOLATILE,
32 NFIT_SPA_PM, 44 NFIT_SPA_PM,
33 NFIT_SPA_DCR, 45 NFIT_SPA_DCR,
@@ -37,7 +49,6 @@ enum nfit_uuids {
37 NFIT_SPA_PDISK, 49 NFIT_SPA_PDISK,
38 NFIT_SPA_PCD, 50 NFIT_SPA_PCD,
39 NFIT_DEV_BUS, 51 NFIT_DEV_BUS,
40 NFIT_DEV_DIMM,
41 NFIT_UUID_MAX, 52 NFIT_UUID_MAX,
42}; 53};
43 54
@@ -110,6 +121,7 @@ struct nfit_mem {
110 struct list_head list; 121 struct list_head list;
111 struct acpi_device *adev; 122 struct acpi_device *adev;
112 unsigned long dsm_mask; 123 unsigned long dsm_mask;
124 int family;
113}; 125};
114 126
115struct acpi_nfit_desc { 127struct acpi_nfit_desc {
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index cb2042a12b76..395a9fbbc69d 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -439,6 +439,12 @@ static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = {
439 .out_num = 3, 439 .out_num = 3,
440 .out_sizes = { 4, 4, UINT_MAX, }, 440 .out_sizes = { 4, 4, UINT_MAX, },
441 }, 441 },
442 [ND_CMD_CALL] = {
443 .in_num = 2,
444 .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
445 .out_num = 1,
446 .out_sizes = { UINT_MAX, },
447 },
442}; 448};
443 449
444const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd) 450const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd)
@@ -473,6 +479,12 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
473 .out_num = 3, 479 .out_num = 3,
474 .out_sizes = { 4, 4, 8, }, 480 .out_sizes = { 4, 4, 8, },
475 }, 481 },
482 [ND_CMD_CALL] = {
483 .in_num = 2,
484 .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
485 .out_num = 1,
486 .out_sizes = { UINT_MAX, },
487 },
476}; 488};
477 489
478const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) 490const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd)
@@ -500,6 +512,10 @@ u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
500 struct nd_cmd_vendor_hdr *hdr = buf; 512 struct nd_cmd_vendor_hdr *hdr = buf;
501 513
502 return hdr->in_length; 514 return hdr->in_length;
515 } else if (cmd == ND_CMD_CALL) {
516 struct nd_cmd_pkg *pkg = buf;
517
518 return pkg->nd_size_in;
503 } 519 }
504 520
505 return UINT_MAX; 521 return UINT_MAX;
@@ -522,6 +538,12 @@ u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
522 return out_field[1]; 538 return out_field[1];
523 else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2) 539 else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2)
524 return out_field[1] - 8; 540 return out_field[1] - 8;
541 else if (cmd == ND_CMD_CALL) {
542 struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field;
543
544 return pkg->nd_size_out;
545 }
546
525 547
526 return UINT_MAX; 548 return UINT_MAX;
527} 549}
@@ -588,6 +610,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
588 unsigned int cmd = _IOC_NR(ioctl_cmd); 610 unsigned int cmd = _IOC_NR(ioctl_cmd);
589 void __user *p = (void __user *) arg; 611 void __user *p = (void __user *) arg;
590 struct device *dev = &nvdimm_bus->dev; 612 struct device *dev = &nvdimm_bus->dev;
613 struct nd_cmd_pkg pkg;
591 const char *cmd_name, *dimm_name; 614 const char *cmd_name, *dimm_name;
592 unsigned long cmd_mask; 615 unsigned long cmd_mask;
593 void *buf; 616 void *buf;
@@ -605,6 +628,11 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
605 dimm_name = "bus"; 628 dimm_name = "bus";
606 } 629 }
607 630
631 if (cmd == ND_CMD_CALL) {
632 if (copy_from_user(&pkg, p, sizeof(pkg)))
633 return -EFAULT;
634 }
635
608 if (!desc || (desc->out_num + desc->in_num == 0) || 636 if (!desc || (desc->out_num + desc->in_num == 0) ||
609 !test_bit(cmd, &cmd_mask)) 637 !test_bit(cmd, &cmd_mask))
610 return -ENOTTY; 638 return -ENOTTY;
@@ -616,6 +644,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
616 case ND_CMD_SET_CONFIG_DATA: 644 case ND_CMD_SET_CONFIG_DATA:
617 case ND_CMD_ARS_START: 645 case ND_CMD_ARS_START:
618 case ND_CMD_CLEAR_ERROR: 646 case ND_CMD_CLEAR_ERROR:
647 case ND_CMD_CALL:
619 dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", 648 dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n",
620 nvdimm ? nvdimm_cmd_name(cmd) 649 nvdimm ? nvdimm_cmd_name(cmd)
621 : nvdimm_bus_cmd_name(cmd)); 650 : nvdimm_bus_cmd_name(cmd));
@@ -643,6 +672,16 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
643 in_len += in_size; 672 in_len += in_size;
644 } 673 }
645 674
675 if (cmd == ND_CMD_CALL) {
676 dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n",
677 __func__, dimm_name, pkg.nd_command,
678 in_len, out_len, buf_len);
679
680 for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++)
681 if (pkg.nd_reserved2[i])
682 return -EINVAL;
683 }
684
646 /* process an output envelope */ 685 /* process an output envelope */
647 for (i = 0; i < desc->out_num; i++) { 686 for (i = 0; i < desc->out_num; i++) {
648 u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, 687 u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i,
diff --git a/include/uapi/linux/ndctl.h b/include/uapi/linux/ndctl.h
index 7cc28ab05b87..45daa0be5ff9 100644
--- a/include/uapi/linux/ndctl.h
+++ b/include/uapi/linux/ndctl.h
@@ -125,6 +125,7 @@ enum {
125 ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7, 125 ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7,
126 ND_CMD_VENDOR_EFFECT_LOG = 8, 126 ND_CMD_VENDOR_EFFECT_LOG = 8,
127 ND_CMD_VENDOR = 9, 127 ND_CMD_VENDOR = 9,
128 ND_CMD_CALL = 10,
128}; 129};
129 130
130enum { 131enum {
@@ -158,6 +159,7 @@ static inline const char *nvdimm_cmd_name(unsigned cmd)
158 [ND_CMD_VENDOR_EFFECT_LOG_SIZE] = "effect_size", 159 [ND_CMD_VENDOR_EFFECT_LOG_SIZE] = "effect_size",
159 [ND_CMD_VENDOR_EFFECT_LOG] = "effect_log", 160 [ND_CMD_VENDOR_EFFECT_LOG] = "effect_log",
160 [ND_CMD_VENDOR] = "vendor", 161 [ND_CMD_VENDOR] = "vendor",
162 [ND_CMD_CALL] = "cmd_call",
161 }; 163 };
162 164
163 if (cmd < ARRAY_SIZE(names) && names[cmd]) 165 if (cmd < ARRAY_SIZE(names) && names[cmd])
@@ -224,4 +226,44 @@ enum ars_masks {
224 ARS_STATUS_MASK = 0x0000FFFF, 226 ARS_STATUS_MASK = 0x0000FFFF,
225 ARS_EXT_STATUS_SHIFT = 16, 227 ARS_EXT_STATUS_SHIFT = 16,
226}; 228};
229
230/*
231 * struct nd_cmd_pkg
232 *
233 * is a wrapper to a quasi pass thru interface for invoking firmware
234 * associated with nvdimms.
235 *
236 * INPUT PARAMETERS
237 *
238 * nd_family corresponds to the firmware (e.g. DSM) interface.
239 *
240 * nd_command are the function index advertised by the firmware.
241 *
242 * nd_size_in is the size of the input parameters being passed to firmware
243 *
244 * OUTPUT PARAMETERS
245 *
246 * nd_fw_size is the size of the data firmware wants to return for
247 * the call. If nd_fw_size is greater than size of nd_size_out, only
248 * the first nd_size_out bytes are returned.
249 */
250
251struct nd_cmd_pkg {
252 __u64 nd_family; /* family of commands */
253 __u64 nd_command;
254 __u32 nd_size_in; /* INPUT: size of input args */
255 __u32 nd_size_out; /* INPUT: size of payload */
256 __u32 nd_reserved2[9]; /* reserved must be zero */
257 __u32 nd_fw_size; /* OUTPUT: size fw wants to return */
258 unsigned char nd_payload[]; /* Contents of call */
259};
260
261/* These NVDIMM families represent pre-standardization command sets */
262#define NVDIMM_FAMILY_INTEL 0
263#define NVDIMM_FAMILY_HPE1 1
264#define NVDIMM_FAMILY_HPE2 2
265
266#define ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\
267 struct nd_cmd_pkg)
268
227#endif /* __NDCTL_H__ */ 269#endif /* __NDCTL_H__ */