diff --git a/sys/dev/nvdimm/nvdimm.c b/sys/dev/nvdimm/nvdimm.c index e7d13cbf20f7..1e4d86cf582a 100644 --- a/sys/dev/nvdimm/nvdimm.c +++ b/sys/dev/nvdimm/nvdimm.c @@ -43,10 +43,12 @@ __FBSDID("$FreeBSD$"); #include #include #include + #include #include #include #include + #include #define _COMPONENT ACPI_OEM @@ -59,7 +61,6 @@ static struct uuid intel_nvdimm_dsm_uuid = #define INTEL_NVDIMM_DSM_GET_LABEL_DATA 5 static devclass_t nvdimm_devclass; -static devclass_t nvdimm_root_devclass; MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory"); static int @@ -325,8 +326,7 @@ nvdimm_attach(device_t dev) nv = device_get_softc(dev); handle = nvdimm_root_get_acpi_handle(dev); - if (handle == NULL) - return (EINVAL); + MPASS(handle != NULL); nv->nv_dev = dev; nv->nv_handle = nvdimm_root_get_device_handle(dev); @@ -380,204 +380,6 @@ nvdimm_resume(device_t dev) return (0); } -static ACPI_STATUS -find_dimm(ACPI_HANDLE handle, UINT32 nesting_level, void *context, - void **return_value) -{ - ACPI_DEVICE_INFO *device_info; - ACPI_STATUS status; - - status = AcpiGetObjectInfo(handle, &device_info); - if (ACPI_FAILURE(status)) - return_ACPI_STATUS(AE_ERROR); - if (device_info->Address == (uintptr_t)context) { - *(ACPI_HANDLE *)return_value = handle; - return_ACPI_STATUS(AE_CTRL_TERMINATE); - } - return_ACPI_STATUS(AE_OK); -} - -static ACPI_HANDLE -get_dimm_acpi_handle(ACPI_HANDLE root_handle, nfit_handle_t adr) -{ - ACPI_HANDLE res; - ACPI_STATUS status; - - res = NULL; - status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, root_handle, 1, find_dimm, - NULL, (void *)(uintptr_t)adr, &res); - if (ACPI_FAILURE(status)) - res = NULL; - return (res); -} - -static int -nvdimm_root_create_devs(device_t dev, ACPI_TABLE_NFIT *nfitbl) -{ - ACPI_HANDLE root_handle, dimm_handle; - device_t child; - nfit_handle_t *dimm_ids, *dimm; - uintptr_t *ivars; - int num_dimm_ids; - - root_handle = acpi_get_handle(dev); - acpi_nfit_get_dimm_ids(nfitbl, &dimm_ids, &num_dimm_ids); - for (dimm = dimm_ids; dimm < dimm_ids + num_dimm_ids; dimm++) { - dimm_handle = get_dimm_acpi_handle(root_handle, *dimm); - child = BUS_ADD_CHILD(dev, 100, "nvdimm", -1); - if (child == NULL) { - device_printf(dev, "failed to create nvdimm\n"); - return (ENXIO); - } - ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX, sizeof(uintptr_t), - M_NVDIMM, M_ZERO | M_WAITOK); - device_set_ivars(child, ivars); - nvdimm_root_set_acpi_handle(child, dimm_handle); - nvdimm_root_set_device_handle(child, *dimm); - } - free(dimm_ids, M_NVDIMM); - return (0); -} - -static int -nvdimm_root_create_spas(struct nvdimm_root_dev *dev, ACPI_TABLE_NFIT *nfitbl) -{ - ACPI_NFIT_SYSTEM_ADDRESS **spas, **spa; - struct SPA_mapping *spa_mapping; - enum SPA_mapping_type spa_type; - int error, num_spas; - - error = 0; - acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas); - for (spa = spas; spa < spas + num_spas; spa++) { - spa_type = nvdimm_spa_type_from_uuid( - (struct uuid *)(*spa)->RangeGuid); - if (spa_type == SPA_TYPE_UNKNOWN) - continue; - spa_mapping = malloc(sizeof(struct SPA_mapping), M_NVDIMM, - M_WAITOK | M_ZERO); - error = nvdimm_spa_init(spa_mapping, *spa, spa_type); - if (error != 0) { - nvdimm_spa_fini(spa_mapping); - free(spa, M_NVDIMM); - break; - } - nvdimm_create_namespaces(spa_mapping, nfitbl); - SLIST_INSERT_HEAD(&dev->spas, spa_mapping, link); - } - free(spas, M_NVDIMM); - return (error); -} - -static char *nvdimm_root_id[] = {"ACPI0012", NULL}; - -static int -nvdimm_root_probe(device_t dev) -{ - int rv; - - if (acpi_disabled("nvdimm")) - return (ENXIO); - rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); - if (rv <= 0) - device_set_desc(dev, "ACPI NVDIMM root device"); - - return (rv); -} - -static int -nvdimm_root_attach(device_t dev) -{ - struct nvdimm_root_dev *root; - ACPI_TABLE_NFIT *nfitbl; - ACPI_STATUS status; - int error; - - status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); - if (ACPI_FAILURE(status)) { - device_printf(dev, "cannot get NFIT\n"); - return (ENXIO); - } - error = nvdimm_root_create_devs(dev, nfitbl); - if (error != 0) - return (error); - error = bus_generic_attach(dev); - if (error != 0) - return (error); - root = device_get_softc(dev); - error = nvdimm_root_create_spas(root, nfitbl); - AcpiPutTable(&nfitbl->Header); - return (error); -} - -static int -nvdimm_root_detach(device_t dev) -{ - struct nvdimm_root_dev *root; - struct SPA_mapping *spa, *next; - device_t *children; - int i, error, num_children; - - root = device_get_softc(dev); - SLIST_FOREACH_SAFE(spa, &root->spas, link, next) { - nvdimm_destroy_namespaces(spa); - nvdimm_spa_fini(spa); - SLIST_REMOVE_HEAD(&root->spas, link); - free(spa, M_NVDIMM); - } - error = bus_generic_detach(dev); - if (error != 0) - return (error); - error = device_get_children(dev, &children, &num_children); - if (error != 0) - return (error); - for (i = 0; i < num_children; i++) - free(device_get_ivars(children[i]), M_NVDIMM); - free(children, M_TEMP); - error = device_delete_children(dev); - return (error); -} - -static int -nvdimm_root_read_ivar(device_t dev, device_t child, int index, - uintptr_t *result) -{ - - if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) - return (ENOENT); - *result = ((uintptr_t *)device_get_ivars(child))[index]; - return (0); -} - -static int -nvdimm_root_write_ivar(device_t dev, device_t child, int index, - uintptr_t value) -{ - - if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) - return (ENOENT); - ((uintptr_t *)device_get_ivars(child))[index] = value; - return (0); -} - -static int -nvdimm_root_child_location_str(device_t dev, device_t child, char *buf, - size_t buflen) -{ - ACPI_HANDLE handle; - int res; - - handle = nvdimm_root_get_acpi_handle(child); - if (handle != NULL) - res = snprintf(buf, buflen, "handle=%s", acpi_name(handle)); - else - res = snprintf(buf, buflen, ""); - - if (res >= buflen) - return (EOVERFLOW); - return (0); -} - static device_method_t nvdimm_methods[] = { DEVMETHOD(device_probe, nvdimm_probe), DEVMETHOD(device_attach, nvdimm_attach), @@ -593,24 +395,6 @@ static driver_t nvdimm_driver = { sizeof(struct nvdimm_dev), }; -static device_method_t nvdimm_root_methods[] = { - DEVMETHOD(device_probe, nvdimm_root_probe), - DEVMETHOD(device_attach, nvdimm_root_attach), - DEVMETHOD(device_detach, nvdimm_root_detach), - DEVMETHOD(bus_add_child, bus_generic_add_child), - DEVMETHOD(bus_read_ivar, nvdimm_root_read_ivar), - DEVMETHOD(bus_write_ivar, nvdimm_root_write_ivar), - DEVMETHOD(bus_child_location_str, nvdimm_root_child_location_str), - DEVMETHOD_END -}; - -static driver_t nvdimm_root_driver = { - "nvdimm_root", - nvdimm_root_methods, - sizeof(struct nvdimm_root_dev), -}; - -DRIVER_MODULE(nvdimm_root, acpi, nvdimm_root_driver, nvdimm_root_devclass, NULL, +DRIVER_MODULE(nvdimm, nvdimm_acpi_root, nvdimm_driver, nvdimm_devclass, NULL, NULL); -DRIVER_MODULE(nvdimm, nvdimm_root, nvdimm_driver, nvdimm_devclass, NULL, NULL); MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); diff --git a/sys/dev/nvdimm/nvdimm_acpi.c b/sys/dev/nvdimm/nvdimm_acpi.c new file mode 100644 index 000000000000..1447c64fb34d --- /dev/null +++ b/sys/dev/nvdimm/nvdimm_acpi.c @@ -0,0 +1,283 @@ +/*- + * Copyright (c) 2017 The FreeBSD Foundation + * Copyright (c) 2018, 2019 Intel Corporation + * + * This software was developed by Konstantin Belousov + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_acpi.h" +#include "opt_ddb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define _COMPONENT ACPI_OEM +ACPI_MODULE_NAME("NVDIMM_ACPI") + +struct nvdimm_root_dev { + SLIST_HEAD(, SPA_mapping) spas; +}; + +static MALLOC_DEFINE(M_NVDIMM_ACPI, "nvdimm_acpi", "NVDIMM ACPI bus memory"); + +static ACPI_STATUS +find_dimm(ACPI_HANDLE handle, UINT32 nesting_level, void *context, + void **return_value) +{ + ACPI_DEVICE_INFO *device_info; + ACPI_STATUS status; + + status = AcpiGetObjectInfo(handle, &device_info); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(AE_ERROR); + if (device_info->Address == (uintptr_t)context) { + *(ACPI_HANDLE *)return_value = handle; + return_ACPI_STATUS(AE_CTRL_TERMINATE); + } + return_ACPI_STATUS(AE_OK); +} + +static ACPI_HANDLE +get_dimm_acpi_handle(ACPI_HANDLE root_handle, nfit_handle_t adr) +{ + ACPI_HANDLE res; + ACPI_STATUS status; + + res = NULL; + status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, root_handle, 1, find_dimm, + NULL, (void *)(uintptr_t)adr, &res); + if (ACPI_FAILURE(status)) + res = NULL; + return (res); +} + +static int +nvdimm_root_create_devs(device_t dev, ACPI_TABLE_NFIT *nfitbl) +{ + ACPI_HANDLE root_handle, dimm_handle; + device_t child; + nfit_handle_t *dimm_ids, *dimm; + uintptr_t *ivars; + int num_dimm_ids; + + root_handle = acpi_get_handle(dev); + acpi_nfit_get_dimm_ids(nfitbl, &dimm_ids, &num_dimm_ids); + for (dimm = dimm_ids; dimm < dimm_ids + num_dimm_ids; dimm++) { + dimm_handle = get_dimm_acpi_handle(root_handle, *dimm); + if (dimm_handle == NULL) + continue; + + child = BUS_ADD_CHILD(dev, 100, "nvdimm", -1); + if (child == NULL) { + device_printf(dev, "failed to create nvdimm\n"); + return (ENXIO); + } + ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX, sizeof(uintptr_t), + M_NVDIMM_ACPI, M_ZERO | M_WAITOK); + device_set_ivars(child, ivars); + nvdimm_root_set_acpi_handle(child, dimm_handle); + nvdimm_root_set_device_handle(child, *dimm); + } + free(dimm_ids, M_NVDIMM_ACPI); + return (0); +} + +static int +nvdimm_root_create_spas(struct nvdimm_root_dev *dev, ACPI_TABLE_NFIT *nfitbl) +{ + ACPI_NFIT_SYSTEM_ADDRESS **spas, **spa; + struct SPA_mapping *spa_mapping; + enum SPA_mapping_type spa_type; + int error, num_spas; + + error = 0; + acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas); + for (spa = spas; spa < spas + num_spas; spa++) { + spa_type = nvdimm_spa_type_from_uuid( + (struct uuid *)(*spa)->RangeGuid); + if (spa_type == SPA_TYPE_UNKNOWN) + continue; + spa_mapping = malloc(sizeof(struct SPA_mapping), M_NVDIMM_ACPI, + M_WAITOK | M_ZERO); + error = nvdimm_spa_init(spa_mapping, *spa, spa_type); + if (error != 0) { + nvdimm_spa_fini(spa_mapping); + free(spa, M_NVDIMM_ACPI); + break; + } + nvdimm_create_namespaces(spa_mapping, nfitbl); + SLIST_INSERT_HEAD(&dev->spas, spa_mapping, link); + } + free(spas, M_NVDIMM_ACPI); + return (error); +} + +static char *nvdimm_root_id[] = {"ACPI0012", NULL}; + +static int +nvdimm_root_probe(device_t dev) +{ + int rv; + + if (acpi_disabled("nvdimm")) + return (ENXIO); + rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); + if (rv <= 0) + device_set_desc(dev, "ACPI NVDIMM root device"); + + return (rv); +} + +static int +nvdimm_root_attach(device_t dev) +{ + struct nvdimm_root_dev *root; + ACPI_TABLE_NFIT *nfitbl; + ACPI_STATUS status; + int error; + + status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); + if (ACPI_FAILURE(status)) { + device_printf(dev, "cannot get NFIT\n"); + return (ENXIO); + } + error = nvdimm_root_create_devs(dev, nfitbl); + if (error != 0) + return (error); + error = bus_generic_attach(dev); + if (error != 0) + return (error); + root = device_get_softc(dev); + error = nvdimm_root_create_spas(root, nfitbl); + AcpiPutTable(&nfitbl->Header); + return (error); +} + +static int +nvdimm_root_detach(device_t dev) +{ + struct nvdimm_root_dev *root; + struct SPA_mapping *spa, *next; + device_t *children; + int i, error, num_children; + + root = device_get_softc(dev); + SLIST_FOREACH_SAFE(spa, &root->spas, link, next) { + nvdimm_destroy_namespaces(spa); + nvdimm_spa_fini(spa); + SLIST_REMOVE_HEAD(&root->spas, link); + free(spa, M_NVDIMM_ACPI); + } + error = bus_generic_detach(dev); + if (error != 0) + return (error); + error = device_get_children(dev, &children, &num_children); + if (error != 0) + return (error); + for (i = 0; i < num_children; i++) + free(device_get_ivars(children[i]), M_NVDIMM_ACPI); + free(children, M_TEMP); + error = device_delete_children(dev); + return (error); +} + +static int +nvdimm_root_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result) +{ + + if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) + return (ENOENT); + *result = ((uintptr_t *)device_get_ivars(child))[index]; + return (0); +} + +static int +nvdimm_root_write_ivar(device_t dev, device_t child, int index, + uintptr_t value) +{ + + if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) + return (ENOENT); + ((uintptr_t *)device_get_ivars(child))[index] = value; + return (0); +} + +static int +nvdimm_root_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + ACPI_HANDLE handle; + int res; + + handle = nvdimm_root_get_acpi_handle(child); + if (handle != NULL) + res = snprintf(buf, buflen, "handle=%s", acpi_name(handle)); + else + res = snprintf(buf, buflen, ""); + + if (res >= buflen) + return (EOVERFLOW); + return (0); +} + +static device_method_t nvdimm_acpi_methods[] = { + DEVMETHOD(device_probe, nvdimm_root_probe), + DEVMETHOD(device_attach, nvdimm_root_attach), + DEVMETHOD(device_detach, nvdimm_root_detach), + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_read_ivar, nvdimm_root_read_ivar), + DEVMETHOD(bus_write_ivar, nvdimm_root_write_ivar), + DEVMETHOD(bus_child_location_str, nvdimm_root_child_location_str), + DEVMETHOD_END +}; + +static driver_t nvdimm_acpi_driver = { + "nvdimm_acpi_root", + nvdimm_acpi_methods, + sizeof(struct nvdimm_root_dev), +}; + +static devclass_t nvdimm_acpi_root_devclass; +DRIVER_MODULE(nvdimm_acpi_root, acpi, nvdimm_acpi_driver, + nvdimm_acpi_root_devclass, NULL, NULL); +MODULE_DEPEND(nvdimm_acpi_root, acpi, 1, 1, 1); diff --git a/sys/dev/nvdimm/nvdimm_var.h b/sys/dev/nvdimm/nvdimm_var.h index 7d1621512a42..99346de42fd8 100644 --- a/sys/dev/nvdimm/nvdimm_var.h +++ b/sys/dev/nvdimm/nvdimm_var.h @@ -80,7 +80,7 @@ _Static_assert(sizeof(struct nvdimm_label) == 256, "Incorrect layout"); typedef uint32_t nfit_handle_t; -enum nvdimm_root_ivar { +enum nvdimm_acpi_ivar { NVDIMM_ROOT_IVAR_ACPI_HANDLE, NVDIMM_ROOT_IVAR_DEVICE_HANDLE, NVDIMM_ROOT_IVAR_MAX, @@ -89,10 +89,6 @@ __BUS_ACCESSOR(nvdimm_root, acpi_handle, NVDIMM_ROOT, ACPI_HANDLE, ACPI_HANDLE) __BUS_ACCESSOR(nvdimm_root, device_handle, NVDIMM_ROOT, DEVICE_HANDLE, nfit_handle_t) -struct nvdimm_root_dev { - SLIST_HEAD(, SPA_mapping) spas; -}; - struct nvdimm_dev { device_t nv_dev; nfit_handle_t nv_handle; diff --git a/sys/modules/nvdimm/Makefile b/sys/modules/nvdimm/Makefile index 2d537cd55dd2..5fd827730d59 100644 --- a/sys/modules/nvdimm/Makefile +++ b/sys/modules/nvdimm/Makefile @@ -4,6 +4,7 @@ KMOD= nvdimm SRCS= nvdimm.c \ + nvdimm_acpi.c \ nvdimm_nfit.c \ nvdimm_ns.c \ nvdimm_spa.c