diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 3369bb8fd9ba..ebe0857ac346 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -183,13 +183,33 @@ static int xlat_bus_status(void *buf, unsigned int cmd, u32 status) 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) { 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) return -EACCES; 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: break; } @@ -205,13 +225,156 @@ static int xlat_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd, { if (!nvdimm) 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, 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 nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); union acpi_object in_obj, in_buf, *out_obj; const struct nd_cmd_desc *desc = NULL; 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) { - struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct acpi_device *adev = nfit_mem->adev; if (!adev) @@ -294,7 +456,21 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, in_buf.buffer.pointer, 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) { dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_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 device *dev = acpi_desc->dev; + union acpi_object *obj; unsigned long dsm_mask; const guid_t *guid; 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)) 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; } @@ -1574,6 +1772,13 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) if (nfit_mem->family == NVDIMM_FAMILY_INTEL) 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 : NULL; nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index f2c6380bdbb2..3976d649f27c 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -140,6 +140,9 @@ struct nfit_mem { struct resource *flush_wpq; unsigned long dsm_mask; int family; + u32 has_lsi:1; + u32 has_lsr:1; + u32 has_lsw:1; }; struct acpi_nfit_desc { diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index 98466d762c8f..f8913b8124b6 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c @@ -55,6 +55,8 @@ static int nvdimm_probe(struct device *dev) goto err; rc = nvdimm_init_config_data(ndd); + if (rc == -EACCES) + nvdimm_set_locked(dev); if (rc) goto err;