Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/nvdimm/nvdimm.c
Show All 37 Lines | |||||
#include <sys/bio.h> | #include <sys/bio.h> | ||||
#include <sys/bitstring.h> | #include <sys/bitstring.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/uuid.h> | #include <sys/uuid.h> | ||||
#include <contrib/dev/acpica/include/acpi.h> | #include <contrib/dev/acpica/include/acpi.h> | ||||
#include <contrib/dev/acpica/include/accommon.h> | #include <contrib/dev/acpica/include/accommon.h> | ||||
#include <contrib/dev/acpica/include/acuuid.h> | #include <contrib/dev/acpica/include/acuuid.h> | ||||
#include <dev/acpica/acpivar.h> | #include <dev/acpica/acpivar.h> | ||||
#include <dev/nvdimm/nvdimm_var.h> | #include <dev/nvdimm/nvdimm_var.h> | ||||
#define _COMPONENT ACPI_OEM | #define _COMPONENT ACPI_OEM | ||||
ACPI_MODULE_NAME("NVDIMM") | ACPI_MODULE_NAME("NVDIMM") | ||||
static struct uuid intel_nvdimm_dsm_uuid = | static struct uuid intel_nvdimm_dsm_uuid = | ||||
{0x4309AC30,0x0D11,0x11E4,0x91,0x91,{0x08,0x00,0x20,0x0C,0x9A,0x66}}; | {0x4309AC30,0x0D11,0x11E4,0x91,0x91,{0x08,0x00,0x20,0x0C,0x9A,0x66}}; | ||||
#define INTEL_NVDIMM_DSM_REV 1 | #define INTEL_NVDIMM_DSM_REV 1 | ||||
#define INTEL_NVDIMM_DSM_GET_LABEL_SIZE 4 | #define INTEL_NVDIMM_DSM_GET_LABEL_SIZE 4 | ||||
#define INTEL_NVDIMM_DSM_GET_LABEL_DATA 5 | #define INTEL_NVDIMM_DSM_GET_LABEL_DATA 5 | ||||
static devclass_t nvdimm_devclass; | static devclass_t nvdimm_devclass; | ||||
static devclass_t nvdimm_root_devclass; | |||||
MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory"); | MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory"); | ||||
static int | static int | ||||
read_label_area_size(struct nvdimm_dev *nv) | read_label_area_size(struct nvdimm_dev *nv) | ||||
{ | { | ||||
ACPI_OBJECT *result_buffer; | ACPI_OBJECT *result_buffer; | ||||
ACPI_HANDLE handle; | ACPI_HANDLE handle; | ||||
ACPI_STATUS status; | ACPI_STATUS status; | ||||
▲ Show 20 Lines • Show All 249 Lines • ▼ Show 20 Lines | nvdimm_attach(device_t dev) | ||||
struct nvdimm_dev *nv; | struct nvdimm_dev *nv; | ||||
ACPI_TABLE_NFIT *nfitbl; | ACPI_TABLE_NFIT *nfitbl; | ||||
ACPI_HANDLE handle; | ACPI_HANDLE handle; | ||||
ACPI_STATUS status; | ACPI_STATUS status; | ||||
int error; | int error; | ||||
nv = device_get_softc(dev); | nv = device_get_softc(dev); | ||||
handle = nvdimm_root_get_acpi_handle(dev); | handle = nvdimm_root_get_acpi_handle(dev); | ||||
if (handle == NULL) | MPASS(handle != NULL); | ||||
return (EINVAL); | |||||
nv->nv_dev = dev; | nv->nv_dev = dev; | ||||
nv->nv_handle = nvdimm_root_get_device_handle(dev); | nv->nv_handle = nvdimm_root_get_device_handle(dev); | ||||
status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); | status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); | ||||
if (ACPI_FAILURE(status)) { | if (ACPI_FAILURE(status)) { | ||||
if (bootverbose) | if (bootverbose) | ||||
device_printf(dev, "cannot get NFIT\n"); | device_printf(dev, "cannot get NFIT\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
Show All 37 Lines | |||||
static int | static int | ||||
nvdimm_resume(device_t dev) | nvdimm_resume(device_t dev) | ||||
{ | { | ||||
return (0); | 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[] = { | static device_method_t nvdimm_methods[] = { | ||||
DEVMETHOD(device_probe, nvdimm_probe), | DEVMETHOD(device_probe, nvdimm_probe), | ||||
DEVMETHOD(device_attach, nvdimm_attach), | DEVMETHOD(device_attach, nvdimm_attach), | ||||
DEVMETHOD(device_detach, nvdimm_detach), | DEVMETHOD(device_detach, nvdimm_detach), | ||||
DEVMETHOD(device_suspend, nvdimm_suspend), | DEVMETHOD(device_suspend, nvdimm_suspend), | ||||
DEVMETHOD(device_resume, nvdimm_resume), | DEVMETHOD(device_resume, nvdimm_resume), | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static driver_t nvdimm_driver = { | static driver_t nvdimm_driver = { | ||||
"nvdimm", | "nvdimm", | ||||
nvdimm_methods, | nvdimm_methods, | ||||
sizeof(struct nvdimm_dev), | sizeof(struct nvdimm_dev), | ||||
}; | }; | ||||
static device_method_t nvdimm_root_methods[] = { | DRIVER_MODULE(nvdimm, nvdimm_acpi_root, nvdimm_driver, nvdimm_devclass, NULL, | ||||
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, | |||||
NULL); | NULL); | ||||
DRIVER_MODULE(nvdimm, nvdimm_root, nvdimm_driver, nvdimm_devclass, NULL, NULL); | |||||
MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); | MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); |