|
@@ -183,13 +183,33 @@ static int xlat_bus_status(void *buf, unsigned int cmd, u32 status)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int xlat_nvdimm_status(void *buf, unsigned int cmd, u32 status)
|
|
|
|
|
|
+#define ACPI_LABELS_LOCKED 3
|
|
|
|
+
|
|
|
|
+static int xlat_nvdimm_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd,
|
|
|
|
+ u32 status)
|
|
{
|
|
{
|
|
|
|
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
|
|
|
+
|
|
switch (cmd) {
|
|
switch (cmd) {
|
|
case ND_CMD_GET_CONFIG_SIZE:
|
|
case ND_CMD_GET_CONFIG_SIZE:
|
|
|
|
+ /*
|
|
|
|
+ * In the _LSI, _LSR, _LSW case the locked status is
|
|
|
|
+ * communicated via the read/write commands
|
|
|
|
+ */
|
|
|
|
+ if (nfit_mem->has_lsi)
|
|
|
|
+ break;
|
|
|
|
+
|
|
if (status >> 16 & ND_CONFIG_LOCKED)
|
|
if (status >> 16 & ND_CONFIG_LOCKED)
|
|
return -EACCES;
|
|
return -EACCES;
|
|
break;
|
|
break;
|
|
|
|
+ case ND_CMD_GET_CONFIG_DATA:
|
|
|
|
+ if (nfit_mem->has_lsr && status == ACPI_LABELS_LOCKED)
|
|
|
|
+ return -EACCES;
|
|
|
|
+ break;
|
|
|
|
+ case ND_CMD_SET_CONFIG_DATA:
|
|
|
|
+ if (nfit_mem->has_lsw && status == ACPI_LABELS_LOCKED)
|
|
|
|
+ return -EACCES;
|
|
|
|
+ break;
|
|
default:
|
|
default:
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -205,13 +225,156 @@ static int xlat_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd,
|
|
{
|
|
{
|
|
if (!nvdimm)
|
|
if (!nvdimm)
|
|
return xlat_bus_status(buf, cmd, status);
|
|
return xlat_bus_status(buf, cmd, status);
|
|
- return xlat_nvdimm_status(buf, cmd, status);
|
|
|
|
|
|
+ return xlat_nvdimm_status(nvdimm, buf, cmd, status);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* convert _LS{I,R} packages to the buffer object acpi_nfit_ctl expects */
|
|
|
|
+static union acpi_object *pkg_to_buf(union acpi_object *pkg)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+ void *dst;
|
|
|
|
+ size_t size = 0;
|
|
|
|
+ union acpi_object *buf = NULL;
|
|
|
|
+
|
|
|
|
+ if (pkg->type != ACPI_TYPE_PACKAGE) {
|
|
|
|
+ WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
|
|
|
|
+ pkg->type);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < pkg->package.count; i++) {
|
|
|
|
+ union acpi_object *obj = &pkg->package.elements[i];
|
|
|
|
+
|
|
|
|
+ if (obj->type == ACPI_TYPE_INTEGER)
|
|
|
|
+ size += 4;
|
|
|
|
+ else if (obj->type == ACPI_TYPE_BUFFER)
|
|
|
|
+ size += obj->buffer.length;
|
|
|
|
+ else {
|
|
|
|
+ WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
|
|
|
|
+ obj->type);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ buf = ACPI_ALLOCATE(sizeof(*buf) + size);
|
|
|
|
+ if (!buf)
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ dst = buf + 1;
|
|
|
|
+ buf->type = ACPI_TYPE_BUFFER;
|
|
|
|
+ buf->buffer.length = size;
|
|
|
|
+ buf->buffer.pointer = dst;
|
|
|
|
+ for (i = 0; i < pkg->package.count; i++) {
|
|
|
|
+ union acpi_object *obj = &pkg->package.elements[i];
|
|
|
|
+
|
|
|
|
+ if (obj->type == ACPI_TYPE_INTEGER) {
|
|
|
|
+ memcpy(dst, &obj->integer.value, 4);
|
|
|
|
+ dst += 4;
|
|
|
|
+ } else if (obj->type == ACPI_TYPE_BUFFER) {
|
|
|
|
+ memcpy(dst, obj->buffer.pointer, obj->buffer.length);
|
|
|
|
+ dst += obj->buffer.length;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+err:
|
|
|
|
+ ACPI_FREE(pkg);
|
|
|
|
+ return buf;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static union acpi_object *int_to_buf(union acpi_object *integer)
|
|
|
|
+{
|
|
|
|
+ union acpi_object *buf = ACPI_ALLOCATE(sizeof(*buf) + 4);
|
|
|
|
+ void *dst = NULL;
|
|
|
|
+
|
|
|
|
+ if (!buf)
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ if (integer->type != ACPI_TYPE_INTEGER) {
|
|
|
|
+ WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
|
|
|
|
+ integer->type);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dst = buf + 1;
|
|
|
|
+ buf->type = ACPI_TYPE_BUFFER;
|
|
|
|
+ buf->buffer.length = 4;
|
|
|
|
+ buf->buffer.pointer = dst;
|
|
|
|
+ memcpy(dst, &integer->integer.value, 4);
|
|
|
|
+err:
|
|
|
|
+ ACPI_FREE(integer);
|
|
|
|
+ return buf;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static union acpi_object *acpi_label_write(acpi_handle handle, u32 offset,
|
|
|
|
+ u32 len, void *data)
|
|
|
|
+{
|
|
|
|
+ acpi_status rc;
|
|
|
|
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
|
|
+ struct acpi_object_list input = {
|
|
|
|
+ .count = 3,
|
|
|
|
+ .pointer = (union acpi_object []) {
|
|
|
|
+ [0] = {
|
|
|
|
+ .integer.type = ACPI_TYPE_INTEGER,
|
|
|
|
+ .integer.value = offset,
|
|
|
|
+ },
|
|
|
|
+ [1] = {
|
|
|
|
+ .integer.type = ACPI_TYPE_INTEGER,
|
|
|
|
+ .integer.value = len,
|
|
|
|
+ },
|
|
|
|
+ [2] = {
|
|
|
|
+ .buffer.type = ACPI_TYPE_BUFFER,
|
|
|
|
+ .buffer.pointer = data,
|
|
|
|
+ .buffer.length = len,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ rc = acpi_evaluate_object(handle, "_LSW", &input, &buf);
|
|
|
|
+ if (ACPI_FAILURE(rc))
|
|
|
|
+ return NULL;
|
|
|
|
+ return int_to_buf(buf.pointer);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static union acpi_object *acpi_label_read(acpi_handle handle, u32 offset,
|
|
|
|
+ u32 len)
|
|
|
|
+{
|
|
|
|
+ acpi_status rc;
|
|
|
|
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
|
|
+ struct acpi_object_list input = {
|
|
|
|
+ .count = 2,
|
|
|
|
+ .pointer = (union acpi_object []) {
|
|
|
|
+ [0] = {
|
|
|
|
+ .integer.type = ACPI_TYPE_INTEGER,
|
|
|
|
+ .integer.value = offset,
|
|
|
|
+ },
|
|
|
|
+ [1] = {
|
|
|
|
+ .integer.type = ACPI_TYPE_INTEGER,
|
|
|
|
+ .integer.value = len,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ rc = acpi_evaluate_object(handle, "_LSR", &input, &buf);
|
|
|
|
+ if (ACPI_FAILURE(rc))
|
|
|
|
+ return NULL;
|
|
|
|
+ return pkg_to_buf(buf.pointer);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static union acpi_object *acpi_label_info(acpi_handle handle)
|
|
|
|
+{
|
|
|
|
+ acpi_status rc;
|
|
|
|
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
|
|
+
|
|
|
|
+ rc = acpi_evaluate_object(handle, "_LSI", NULL, &buf);
|
|
|
|
+ if (ACPI_FAILURE(rc))
|
|
|
|
+ return NULL;
|
|
|
|
+ return pkg_to_buf(buf.pointer);
|
|
}
|
|
}
|
|
|
|
|
|
int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
|
int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
|
unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
|
|
unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
|
|
{
|
|
{
|
|
struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
|
|
struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc);
|
|
|
|
+ struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
|
union acpi_object in_obj, in_buf, *out_obj;
|
|
union acpi_object in_obj, in_buf, *out_obj;
|
|
const struct nd_cmd_desc *desc = NULL;
|
|
const struct nd_cmd_desc *desc = NULL;
|
|
struct device *dev = acpi_desc->dev;
|
|
struct device *dev = acpi_desc->dev;
|
|
@@ -235,7 +398,6 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
|
}
|
|
}
|
|
|
|
|
|
if (nvdimm) {
|
|
if (nvdimm) {
|
|
- struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
|
|
|
struct acpi_device *adev = nfit_mem->adev;
|
|
struct acpi_device *adev = nfit_mem->adev;
|
|
|
|
|
|
if (!adev)
|
|
if (!adev)
|
|
@@ -294,7 +456,21 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
|
|
in_buf.buffer.pointer,
|
|
in_buf.buffer.pointer,
|
|
min_t(u32, 256, in_buf.buffer.length), true);
|
|
min_t(u32, 256, in_buf.buffer.length), true);
|
|
|
|
|
|
- out_obj = acpi_evaluate_dsm(handle, guid, 1, func, &in_obj);
|
|
|
|
|
|
+ /* call the BIOS, prefer the named methods over _DSM if available */
|
|
|
|
+ if (cmd == ND_CMD_GET_CONFIG_SIZE && nfit_mem->has_lsi)
|
|
|
|
+ out_obj = acpi_label_info(handle);
|
|
|
|
+ else if (cmd == ND_CMD_GET_CONFIG_DATA && nfit_mem->has_lsr) {
|
|
|
|
+ struct nd_cmd_get_config_data_hdr *p = buf;
|
|
|
|
+
|
|
|
|
+ out_obj = acpi_label_read(handle, p->in_offset, p->in_length);
|
|
|
|
+ } else if (cmd == ND_CMD_SET_CONFIG_DATA && nfit_mem->has_lsw) {
|
|
|
|
+ struct nd_cmd_set_config_hdr *p = buf;
|
|
|
|
+
|
|
|
|
+ out_obj = acpi_label_write(handle, p->in_offset, p->in_length,
|
|
|
|
+ p->in_buf);
|
|
|
|
+ } else
|
|
|
|
+ out_obj = acpi_evaluate_dsm(handle, guid, 1, func, &in_obj);
|
|
|
|
+
|
|
if (!out_obj) {
|
|
if (!out_obj) {
|
|
dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
|
|
dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name,
|
|
cmd_name);
|
|
cmd_name);
|
|
@@ -1431,6 +1607,7 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
|
|
{
|
|
{
|
|
struct acpi_device *adev, *adev_dimm;
|
|
struct acpi_device *adev, *adev_dimm;
|
|
struct device *dev = acpi_desc->dev;
|
|
struct device *dev = acpi_desc->dev;
|
|
|
|
+ union acpi_object *obj;
|
|
unsigned long dsm_mask;
|
|
unsigned long dsm_mask;
|
|
const guid_t *guid;
|
|
const guid_t *guid;
|
|
int i;
|
|
int i;
|
|
@@ -1496,6 +1673,27 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
|
|
if (acpi_check_dsm(adev_dimm->handle, guid, 1, 1ULL << i))
|
|
if (acpi_check_dsm(adev_dimm->handle, guid, 1, 1ULL << i))
|
|
set_bit(i, &nfit_mem->dsm_mask);
|
|
set_bit(i, &nfit_mem->dsm_mask);
|
|
|
|
|
|
|
|
+ obj = acpi_label_info(adev_dimm->handle);
|
|
|
|
+ if (obj) {
|
|
|
|
+ ACPI_FREE(obj);
|
|
|
|
+ nfit_mem->has_lsi = 1;
|
|
|
|
+ dev_dbg(dev, "%s: has _LSI\n", dev_name(&adev_dimm->dev));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ obj = acpi_label_read(adev_dimm->handle, 0, 0);
|
|
|
|
+ if (obj) {
|
|
|
|
+ ACPI_FREE(obj);
|
|
|
|
+ nfit_mem->has_lsr = 1;
|
|
|
|
+ dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ obj = acpi_label_write(adev_dimm->handle, 0, 0, NULL);
|
|
|
|
+ if (obj) {
|
|
|
|
+ ACPI_FREE(obj);
|
|
|
|
+ nfit_mem->has_lsw = 1;
|
|
|
|
+ dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev));
|
|
|
|
+ }
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1574,6 +1772,13 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
|
|
if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
|
|
if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
|
|
cmd_mask |= nfit_mem->dsm_mask;
|
|
cmd_mask |= nfit_mem->dsm_mask;
|
|
|
|
|
|
|
|
+ if (nfit_mem->has_lsi)
|
|
|
|
+ set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
|
|
|
|
+ if (nfit_mem->has_lsr)
|
|
|
|
+ set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
|
|
|
|
+ if (nfit_mem->has_lsw)
|
|
|
|
+ set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
|
|
|
|
+
|
|
flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush
|
|
flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush
|
|
: NULL;
|
|
: NULL;
|
|
nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
|
|
nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
|