Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f716d05 authored by Dan Williams's avatar Dan Williams
Browse files

Merge branch 'for-4.7/dsm' into libnvdimm-for-next

parents 2159669f a94e3fbe
Loading
Loading
Loading
Loading
+127 −18
Original line number Diff line number Diff line
@@ -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)
+15 −3
Original line number Diff line number Diff line
@@ -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,
@@ -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);
};
+2 −2
Original line number Diff line number Diff line
@@ -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;
+43 −4
Original line number Diff line number Diff line
@@ -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,
+1 −1
Original line number Diff line number Diff line
@@ -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