Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/nvdimm/nvdimm.c
Show First 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
free(dimms, M_TEMP); | free(dimms, M_TEMP); | ||||
return (res); | return (res); | ||||
} | } | ||||
static int | static int | ||||
nvdimm_parse_flush_addr(void *nfitsubtbl, void *arg) | |||||
{ | |||||
ACPI_NFIT_FLUSH_ADDRESS *nfitflshaddr; | |||||
struct nvdimm_dev *nv; | |||||
int i; | |||||
nfitflshaddr = nfitsubtbl; | |||||
nv = arg; | |||||
if (nfitflshaddr->DeviceHandle != nv->nv_handle) | |||||
return (0); | |||||
MPASS(nv->nv_flush_addr == NULL && nv->nv_flush_addr_cnt == 0); | |||||
nv->nv_flush_addr = mallocarray(nfitflshaddr->HintCount, | |||||
sizeof(uint64_t *), M_NVDIMM, M_WAITOK); | |||||
for (i = 0; i < nfitflshaddr->HintCount; i++) | |||||
nv->nv_flush_addr[i] = (uint64_t *)nfitflshaddr->HintAddress[i]; | |||||
nv->nv_flush_addr_cnt = nfitflshaddr->HintCount; | |||||
return (0); | |||||
} | |||||
int | |||||
nvdimm_iterate_nfit(ACPI_TABLE_NFIT *nfitbl, enum AcpiNfitType type, | |||||
int (*cb)(void *, void *), void *arg) | |||||
{ | |||||
ACPI_NFIT_HEADER *nfithdr; | |||||
ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr; | |||||
ACPI_NFIT_MEMORY_MAP *nfitmap; | |||||
ACPI_NFIT_INTERLEAVE *nfitintrl; | |||||
ACPI_NFIT_SMBIOS *nfitsmbios; | |||||
ACPI_NFIT_CONTROL_REGION *nfitctlreg; | |||||
ACPI_NFIT_DATA_REGION *nfitdtreg; | |||||
ACPI_NFIT_FLUSH_ADDRESS *nfitflshaddr; | |||||
char *ptr; | |||||
int error; | |||||
error = 0; | |||||
for (ptr = (char *)(nfitbl + 1); | |||||
ptr < (char *)nfitbl + nfitbl->Header.Length; | |||||
ptr += nfithdr->Length) { | |||||
nfithdr = (ACPI_NFIT_HEADER *)ptr; | |||||
if (nfithdr->Type != type) | |||||
continue; | |||||
switch (nfithdr->Type) { | |||||
case ACPI_NFIT_TYPE_SYSTEM_ADDRESS: | |||||
nfitaddr = __containerof(nfithdr, | |||||
ACPI_NFIT_SYSTEM_ADDRESS, Header); | |||||
error = cb(nfitaddr, arg); | |||||
break; | |||||
case ACPI_NFIT_TYPE_MEMORY_MAP: | |||||
nfitmap = __containerof(nfithdr, | |||||
ACPI_NFIT_MEMORY_MAP, Header); | |||||
error = cb(nfitmap, arg); | |||||
break; | |||||
case ACPI_NFIT_TYPE_INTERLEAVE: | |||||
nfitintrl = __containerof(nfithdr, | |||||
ACPI_NFIT_INTERLEAVE, Header); | |||||
error = cb(nfitintrl, arg); | |||||
break; | |||||
case ACPI_NFIT_TYPE_SMBIOS: | |||||
nfitsmbios = __containerof(nfithdr, | |||||
ACPI_NFIT_SMBIOS, Header); | |||||
error = cb(nfitsmbios, arg); | |||||
break; | |||||
case ACPI_NFIT_TYPE_CONTROL_REGION: | |||||
nfitctlreg = __containerof(nfithdr, | |||||
ACPI_NFIT_CONTROL_REGION, Header); | |||||
error = cb(nfitctlreg, arg); | |||||
break; | |||||
case ACPI_NFIT_TYPE_DATA_REGION: | |||||
nfitdtreg = __containerof(nfithdr, | |||||
ACPI_NFIT_DATA_REGION, Header); | |||||
error = cb(nfitdtreg, arg); | |||||
break; | |||||
case ACPI_NFIT_TYPE_FLUSH_ADDRESS: | |||||
nfitflshaddr = __containerof(nfithdr, | |||||
ACPI_NFIT_FLUSH_ADDRESS, Header); | |||||
error = cb(nfitflshaddr, arg); | |||||
break; | |||||
case ACPI_NFIT_TYPE_RESERVED: | |||||
default: | |||||
if (bootverbose) | |||||
printf("NFIT subtype %d unknown\n", | |||||
nfithdr->Type); | |||||
error = 0; | |||||
break; | |||||
} | |||||
if (error != 0) | |||||
break; | |||||
} | |||||
return (error); | |||||
} | |||||
static int | |||||
nvdimm_probe(device_t dev) | nvdimm_probe(device_t dev) | ||||
{ | { | ||||
return (BUS_PROBE_NOWILDCARD); | return (BUS_PROBE_NOWILDCARD); | ||||
} | } | ||||
static int | static int | ||||
nvdimm_attach(device_t dev) | nvdimm_attach(device_t dev) | ||||
Show All 11 Lines | nvdimm_attach(device_t 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); | ||||
} | } | ||||
nvdimm_iterate_nfit(nfitbl, ACPI_NFIT_TYPE_FLUSH_ADDRESS, | acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr, | ||||
nvdimm_parse_flush_addr, nv); | &nv->nv_flush_addr_cnt); | ||||
AcpiPutTable(&nfitbl->Header); | AcpiPutTable(&nfitbl->Header); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
nvdimm_detach(device_t dev) | nvdimm_detach(device_t dev) | ||||
{ | { | ||||
struct nvdimm_dev *nv; | struct nvdimm_dev *nv; | ||||
Show All 12 Lines | |||||
static int | static int | ||||
nvdimm_resume(device_t dev) | nvdimm_resume(device_t dev) | ||||
{ | { | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static ACPI_STATUS | ||||
nvdimm_root_create_spa(void *nfitsubtbl, void *arg) | find_dimm(ACPI_HANDLE handle, UINT32 nesting_level, void *context, | ||||
void **return_value) | |||||
{ | { | ||||
enum SPA_mapping_type spa_type; | ACPI_DEVICE_INFO *device_info; | ||||
ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr; | ACPI_STATUS status; | ||||
struct SPA_mapping *spa; | |||||
struct nvdimm_root_dev *dev; | |||||
int error; | |||||
nfitaddr = nfitsubtbl; | status = AcpiGetObjectInfo(handle, &device_info); | ||||
dev = arg; | if (ACPI_FAILURE(status)) | ||||
spa_type = nvdimm_spa_type_from_uuid( | return_ACPI_STATUS(AE_ERROR); | ||||
(struct uuid *)nfitaddr->RangeGuid); | if (device_info->Address == (uintptr_t)context) { | ||||
if (spa_type == SPA_TYPE_UNKNOWN) | *(ACPI_HANDLE *)return_value = handle; | ||||
return (0); | return_ACPI_STATUS(AE_CTRL_TERMINATE); | ||||
spa = malloc(sizeof(struct SPA_mapping), M_NVDIMM, M_WAITOK | M_ZERO); | |||||
error = nvdimm_spa_init(spa, nfitaddr, spa_type); | |||||
if (error != 0) { | |||||
nvdimm_spa_fini(spa); | |||||
free(spa, M_NVDIMM); | |||||
} | } | ||||
SLIST_INSERT_HEAD(&dev->spas, spa, link); | return_ACPI_STATUS(AE_OK); | ||||
return (0); | |||||
} | } | ||||
static ACPI_STATUS | static ACPI_HANDLE | ||||
nvdimm_root_create_dev(ACPI_HANDLE handle, UINT32 nesting_level, void *context, | get_dimm_acpi_handle(ACPI_HANDLE root_handle, nfit_handle_t adr) | ||||
void **return_value) | |||||
{ | { | ||||
ACPI_HANDLE res; | |||||
ACPI_STATUS status; | ACPI_STATUS status; | ||||
ACPI_DEVICE_INFO *device_info; | |||||
device_t parent, child; | 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; | uintptr_t *ivars; | ||||
int num_dimm_ids; | |||||
parent = context; | root_handle = acpi_get_handle(dev); | ||||
child = BUS_ADD_CHILD(parent, 100, "nvdimm", -1); | 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) { | if (child == NULL) { | ||||
device_printf(parent, "failed to create nvdimm\n"); | device_printf(dev, "failed to create nvdimm\n"); | ||||
return_ACPI_STATUS(AE_ERROR); | return (ENXIO); | ||||
} | } | ||||
status = AcpiGetObjectInfo(handle, &device_info); | ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX, sizeof(uintptr_t), | ||||
if (ACPI_FAILURE(status)) { | |||||
device_printf(parent, "failed to get nvdimm device info\n"); | |||||
return_ACPI_STATUS(AE_ERROR); | |||||
} | |||||
ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX - 1, sizeof(uintptr_t), | |||||
M_NVDIMM, M_ZERO | M_WAITOK); | M_NVDIMM, M_ZERO | M_WAITOK); | ||||
device_set_ivars(child, ivars); | device_set_ivars(child, ivars); | ||||
nvdimm_root_set_acpi_handle(child, handle); | nvdimm_root_set_acpi_handle(child, dimm_handle); | ||||
nvdimm_root_set_device_handle(child, device_info->Address); | nvdimm_root_set_device_handle(child, *dimm); | ||||
return_ACPI_STATUS(AE_OK); | |||||
} | } | ||||
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; | |||||
} | |||||
SLIST_INSERT_HEAD(&dev->spas, spa_mapping, link); | |||||
} | |||||
free(spas, M_NVDIMM); | |||||
return (error); | |||||
} | |||||
static char *nvdimm_root_id[] = {"ACPI0012", NULL}; | static char *nvdimm_root_id[] = {"ACPI0012", NULL}; | ||||
static int | static int | ||||
nvdimm_root_probe(device_t dev) | nvdimm_root_probe(device_t dev) | ||||
{ | { | ||||
int rv; | int rv; | ||||
if (acpi_disabled("nvdimm")) | if (acpi_disabled("nvdimm")) | ||||
return (ENXIO); | return (ENXIO); | ||||
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); | rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); | ||||
if (rv <= 0) | if (rv <= 0) | ||||
device_set_desc(dev, "ACPI NVDIMM root device"); | device_set_desc(dev, "ACPI NVDIMM root device"); | ||||
return (rv); | return (rv); | ||||
} | } | ||||
static int | static int | ||||
nvdimm_root_attach(device_t dev) | nvdimm_root_attach(device_t dev) | ||||
{ | { | ||||
ACPI_HANDLE handle; | struct nvdimm_root_dev *root; | ||||
ACPI_STATUS status; | |||||
ACPI_TABLE_NFIT *nfitbl; | ACPI_TABLE_NFIT *nfitbl; | ||||
ACPI_STATUS status; | |||||
int error; | int error; | ||||
handle = acpi_get_handle(dev); | |||||
status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, | |||||
nvdimm_root_create_dev, NULL, dev, NULL); | |||||
if (ACPI_FAILURE(status)) | |||||
device_printf(dev, "failed adding children\n"); | |||||
error = bus_generic_attach(dev); | |||||
if (error != 0) | |||||
return (error); | |||||
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)) { | ||||
device_printf(dev, "cannot get NFIT\n"); | device_printf(dev, "cannot get NFIT\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
error = nvdimm_iterate_nfit(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, | error = nvdimm_root_create_devs(dev, nfitbl); | ||||
nvdimm_root_create_spa, device_get_softc(dev)); | 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); | AcpiPutTable(&nfitbl->Header); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
nvdimm_root_detach(device_t dev) | nvdimm_root_detach(device_t dev) | ||||
{ | { | ||||
struct nvdimm_root_dev *root; | struct nvdimm_root_dev *root; | ||||
▲ Show 20 Lines • Show All 80 Lines • Show Last 20 Lines |