Loading drivers/acpi/nfit.c +127 −18 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading Loading @@ -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) { Loading @@ -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, }; Loading Loading @@ -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; Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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) Loading drivers/acpi/nfit.h +15 −3 Original line number Diff line number Diff line Loading @@ -21,13 +21,25 @@ #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, Loading @@ -37,7 +49,6 @@ enum nfit_uuids { NFIT_SPA_PDISK, NFIT_SPA_PCD, NFIT_DEV_BUS, NFIT_DEV_DIMM, NFIT_UUID_MAX, }; Loading Loading @@ -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 { Loading @@ -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); }; Loading drivers/acpi/utils.c +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading drivers/nvdimm/bus.c +43 −4 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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) Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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) */ Loading @@ -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)); Loading Loading @@ -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, Loading drivers/nvdimm/core.c +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading
drivers/acpi/nfit.c +127 −18 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading Loading @@ -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) { Loading @@ -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, }; Loading Loading @@ -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; Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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) Loading
drivers/acpi/nfit.h +15 −3 Original line number Diff line number Diff line Loading @@ -21,13 +21,25 @@ #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, Loading @@ -37,7 +49,6 @@ enum nfit_uuids { NFIT_SPA_PDISK, NFIT_SPA_PCD, NFIT_DEV_BUS, NFIT_DEV_DIMM, NFIT_UUID_MAX, }; Loading Loading @@ -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 { Loading @@ -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); }; Loading
drivers/acpi/utils.c +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading
drivers/nvdimm/bus.c +43 −4 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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) Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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) */ Loading @@ -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)); Loading Loading @@ -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, Loading
drivers/nvdimm/core.c +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading