diff options
-rw-r--r-- | drivers/acpi/nfit.c | 101 | ||||
-rw-r--r-- | drivers/acpi/nfit.h | 14 | ||||
-rw-r--r-- | drivers/nvdimm/bus.c | 39 | ||||
-rw-r--r-- | include/uapi/linux/ndctl.h | 42 |
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 | ||
30 | enum nfit_uuids { | 38 | enum 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 | ||
115 | struct acpi_nfit_desc { | 127 | struct 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 | ||
444 | const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd) | 450 | const 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 | ||
478 | const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) | 490 | const 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 | ||
130 | enum { | 131 | enum { |
@@ -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 | |||
251 | struct 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__ */ |