16db7f8e5SKonstantin Belousov /*- 26db7f8e5SKonstantin Belousov * Copyright (c) 2017 The FreeBSD Foundation 36db7f8e5SKonstantin Belousov * All rights reserved. 4fc4a961aSKonstantin Belousov * Copyright (c) 2018, 2019 Intel Corporation 56db7f8e5SKonstantin Belousov * 66db7f8e5SKonstantin Belousov * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 76db7f8e5SKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 86db7f8e5SKonstantin Belousov * 96db7f8e5SKonstantin Belousov * Redistribution and use in source and binary forms, with or without 106db7f8e5SKonstantin Belousov * modification, are permitted provided that the following conditions 116db7f8e5SKonstantin Belousov * are met: 126db7f8e5SKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 136db7f8e5SKonstantin Belousov * notice, this list of conditions and the following disclaimer. 146db7f8e5SKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 156db7f8e5SKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 166db7f8e5SKonstantin Belousov * documentation and/or other materials provided with the distribution. 176db7f8e5SKonstantin Belousov * 186db7f8e5SKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 196db7f8e5SKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 206db7f8e5SKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 216db7f8e5SKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 226db7f8e5SKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 236db7f8e5SKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 246db7f8e5SKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 256db7f8e5SKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 266db7f8e5SKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 276db7f8e5SKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 286db7f8e5SKonstantin Belousov * SUCH DAMAGE. 296db7f8e5SKonstantin Belousov */ 306db7f8e5SKonstantin Belousov 316db7f8e5SKonstantin Belousov #include <sys/cdefs.h> 326db7f8e5SKonstantin Belousov __FBSDID("$FreeBSD$"); 336db7f8e5SKonstantin Belousov 346db7f8e5SKonstantin Belousov #include "opt_acpi.h" 356db7f8e5SKonstantin Belousov #include "opt_ddb.h" 366db7f8e5SKonstantin Belousov 376db7f8e5SKonstantin Belousov #include <sys/param.h> 38c79cee71SKyle Evans #include <sys/systm.h> 396db7f8e5SKonstantin Belousov #include <sys/bio.h> 40ad30b2f2SBen Widawsky #include <sys/bitstring.h> 416db7f8e5SKonstantin Belousov #include <sys/bus.h> 426db7f8e5SKonstantin Belousov #include <sys/kernel.h> 436db7f8e5SKonstantin Belousov #include <sys/lock.h> 446db7f8e5SKonstantin Belousov #include <sys/malloc.h> 456db7f8e5SKonstantin Belousov #include <sys/module.h> 46bdde49b7SRavi Pokala #include <sys/sbuf.h> 47bdde49b7SRavi Pokala #include <sys/sysctl.h> 486db7f8e5SKonstantin Belousov #include <sys/uuid.h> 49963c89ffSConrad Meyer 506db7f8e5SKonstantin Belousov #include <contrib/dev/acpica/include/acpi.h> 516db7f8e5SKonstantin Belousov #include <contrib/dev/acpica/include/accommon.h> 526db7f8e5SKonstantin Belousov #include <contrib/dev/acpica/include/acuuid.h> 536db7f8e5SKonstantin Belousov #include <dev/acpica/acpivar.h> 54963c89ffSConrad Meyer 556db7f8e5SKonstantin Belousov #include <dev/nvdimm/nvdimm_var.h> 566db7f8e5SKonstantin Belousov 576db7f8e5SKonstantin Belousov #define _COMPONENT ACPI_OEM 586db7f8e5SKonstantin Belousov ACPI_MODULE_NAME("NVDIMM") 596db7f8e5SKonstantin Belousov 60ad30b2f2SBen Widawsky static struct uuid intel_nvdimm_dsm_uuid = 61ad30b2f2SBen Widawsky {0x4309AC30,0x0D11,0x11E4,0x91,0x91,{0x08,0x00,0x20,0x0C,0x9A,0x66}}; 62ad30b2f2SBen Widawsky #define INTEL_NVDIMM_DSM_REV 1 63ad30b2f2SBen Widawsky #define INTEL_NVDIMM_DSM_GET_LABEL_SIZE 4 64ad30b2f2SBen Widawsky #define INTEL_NVDIMM_DSM_GET_LABEL_DATA 5 65ad30b2f2SBen Widawsky 666db7f8e5SKonstantin Belousov MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory"); 676db7f8e5SKonstantin Belousov 68ad30b2f2SBen Widawsky static int 69ad30b2f2SBen Widawsky read_label_area_size(struct nvdimm_dev *nv) 70ad30b2f2SBen Widawsky { 71ad30b2f2SBen Widawsky ACPI_OBJECT *result_buffer; 72ad30b2f2SBen Widawsky ACPI_HANDLE handle; 73ad30b2f2SBen Widawsky ACPI_STATUS status; 74ad30b2f2SBen Widawsky ACPI_BUFFER result; 75ad30b2f2SBen Widawsky uint32_t *out; 76ad30b2f2SBen Widawsky int error; 77ad30b2f2SBen Widawsky 78ad30b2f2SBen Widawsky handle = nvdimm_root_get_acpi_handle(nv->nv_dev); 79ad30b2f2SBen Widawsky if (handle == NULL) 80ad30b2f2SBen Widawsky return (ENODEV); 81ad30b2f2SBen Widawsky result.Length = ACPI_ALLOCATE_BUFFER; 82ad30b2f2SBen Widawsky result.Pointer = NULL; 83ad30b2f2SBen Widawsky status = acpi_EvaluateDSM(handle, (uint8_t *)&intel_nvdimm_dsm_uuid, 84ad30b2f2SBen Widawsky INTEL_NVDIMM_DSM_REV, INTEL_NVDIMM_DSM_GET_LABEL_SIZE, NULL, 85ad30b2f2SBen Widawsky &result); 86ad30b2f2SBen Widawsky error = ENXIO; 87ad30b2f2SBen Widawsky if (ACPI_SUCCESS(status) && result.Pointer != NULL && 88ad30b2f2SBen Widawsky result.Length >= sizeof(ACPI_OBJECT)) { 89ad30b2f2SBen Widawsky result_buffer = result.Pointer; 90ad30b2f2SBen Widawsky if (result_buffer->Type == ACPI_TYPE_BUFFER && 91ad30b2f2SBen Widawsky result_buffer->Buffer.Length >= 12) { 92ad30b2f2SBen Widawsky out = (uint32_t *)result_buffer->Buffer.Pointer; 93ad30b2f2SBen Widawsky nv->label_area_size = out[1]; 94ad30b2f2SBen Widawsky nv->max_label_xfer = out[2]; 95ad30b2f2SBen Widawsky error = 0; 96ad30b2f2SBen Widawsky } 97ad30b2f2SBen Widawsky } 98ad30b2f2SBen Widawsky if (result.Pointer != NULL) 99ad30b2f2SBen Widawsky AcpiOsFree(result.Pointer); 100ad30b2f2SBen Widawsky return (error); 101ad30b2f2SBen Widawsky } 102ad30b2f2SBen Widawsky 103ad30b2f2SBen Widawsky static int 104ad30b2f2SBen Widawsky read_label_area(struct nvdimm_dev *nv, uint8_t *dest, off_t offset, 105ad30b2f2SBen Widawsky off_t length) 106ad30b2f2SBen Widawsky { 107ad30b2f2SBen Widawsky ACPI_BUFFER result; 108ad30b2f2SBen Widawsky ACPI_HANDLE handle; 109ad30b2f2SBen Widawsky ACPI_OBJECT params_pkg, params_buf, *result_buf; 110ad30b2f2SBen Widawsky ACPI_STATUS status; 111ad30b2f2SBen Widawsky uint32_t params[2]; 112ad30b2f2SBen Widawsky off_t to_read; 113ad30b2f2SBen Widawsky int error; 114ad30b2f2SBen Widawsky 115ad30b2f2SBen Widawsky error = 0; 116ad30b2f2SBen Widawsky handle = nvdimm_root_get_acpi_handle(nv->nv_dev); 117ad30b2f2SBen Widawsky if (offset < 0 || length <= 0 || 118ad30b2f2SBen Widawsky offset + length > nv->label_area_size || 119ad30b2f2SBen Widawsky handle == NULL) 120ad30b2f2SBen Widawsky return (ENODEV); 121ad30b2f2SBen Widawsky params_pkg.Type = ACPI_TYPE_PACKAGE; 122ad30b2f2SBen Widawsky params_pkg.Package.Count = 1; 123ad30b2f2SBen Widawsky params_pkg.Package.Elements = ¶ms_buf; 124ad30b2f2SBen Widawsky params_buf.Type = ACPI_TYPE_BUFFER; 125ad30b2f2SBen Widawsky params_buf.Buffer.Length = sizeof(params); 126ad30b2f2SBen Widawsky params_buf.Buffer.Pointer = (UINT8 *)params; 127ad30b2f2SBen Widawsky while (length > 0) { 128ad30b2f2SBen Widawsky to_read = MIN(length, nv->max_label_xfer); 129ad30b2f2SBen Widawsky params[0] = offset; 130ad30b2f2SBen Widawsky params[1] = to_read; 131ad30b2f2SBen Widawsky result.Length = ACPI_ALLOCATE_BUFFER; 132ad30b2f2SBen Widawsky result.Pointer = NULL; 133ad30b2f2SBen Widawsky status = acpi_EvaluateDSM(handle, 134ad30b2f2SBen Widawsky (uint8_t *)&intel_nvdimm_dsm_uuid, INTEL_NVDIMM_DSM_REV, 135ad30b2f2SBen Widawsky INTEL_NVDIMM_DSM_GET_LABEL_DATA, ¶ms_pkg, &result); 136ad30b2f2SBen Widawsky if (ACPI_FAILURE(status) || 137ad30b2f2SBen Widawsky result.Length < sizeof(ACPI_OBJECT) || 138ad30b2f2SBen Widawsky result.Pointer == NULL) { 139ad30b2f2SBen Widawsky error = ENXIO; 140ad30b2f2SBen Widawsky break; 141ad30b2f2SBen Widawsky } 142ad30b2f2SBen Widawsky result_buf = (ACPI_OBJECT *)result.Pointer; 143ad30b2f2SBen Widawsky if (result_buf->Type != ACPI_TYPE_BUFFER || 144ad30b2f2SBen Widawsky result_buf->Buffer.Pointer == NULL || 145ad30b2f2SBen Widawsky result_buf->Buffer.Length != 4 + to_read || 146ad30b2f2SBen Widawsky ((uint16_t *)result_buf->Buffer.Pointer)[0] != 0) { 147ad30b2f2SBen Widawsky error = ENXIO; 148ad30b2f2SBen Widawsky break; 149ad30b2f2SBen Widawsky } 150ad30b2f2SBen Widawsky bcopy(result_buf->Buffer.Pointer + 4, dest, to_read); 151ad30b2f2SBen Widawsky dest += to_read; 152ad30b2f2SBen Widawsky offset += to_read; 153ad30b2f2SBen Widawsky length -= to_read; 154ad30b2f2SBen Widawsky if (result.Pointer != NULL) { 155ad30b2f2SBen Widawsky AcpiOsFree(result.Pointer); 156ad30b2f2SBen Widawsky result.Pointer = NULL; 157ad30b2f2SBen Widawsky } 158ad30b2f2SBen Widawsky } 159ad30b2f2SBen Widawsky if (result.Pointer != NULL) 160ad30b2f2SBen Widawsky AcpiOsFree(result.Pointer); 161ad30b2f2SBen Widawsky return (error); 162ad30b2f2SBen Widawsky } 163ad30b2f2SBen Widawsky 164ad30b2f2SBen Widawsky static uint64_t 165ad30b2f2SBen Widawsky fletcher64(const void *data, size_t length) 166ad30b2f2SBen Widawsky { 167ad30b2f2SBen Widawsky size_t i; 168ad30b2f2SBen Widawsky uint32_t a, b; 169ad30b2f2SBen Widawsky const uint32_t *d; 170ad30b2f2SBen Widawsky 171ad30b2f2SBen Widawsky a = 0; 172ad30b2f2SBen Widawsky b = 0; 173ad30b2f2SBen Widawsky d = (const uint32_t *)data; 174ad30b2f2SBen Widawsky length = length / sizeof(uint32_t); 175ad30b2f2SBen Widawsky for (i = 0; i < length; i++) { 176ad30b2f2SBen Widawsky a += d[i]; 177ad30b2f2SBen Widawsky b += a; 178ad30b2f2SBen Widawsky } 179ad30b2f2SBen Widawsky return ((uint64_t)b << 32 | a); 180ad30b2f2SBen Widawsky } 181ad30b2f2SBen Widawsky 182ad30b2f2SBen Widawsky static bool 183ad30b2f2SBen Widawsky label_index_is_valid(struct nvdimm_label_index *index, uint32_t max_labels, 184ad30b2f2SBen Widawsky size_t size, size_t offset) 185ad30b2f2SBen Widawsky { 186ad30b2f2SBen Widawsky uint64_t checksum; 187ad30b2f2SBen Widawsky 188178d6bc8SD Scott Phillips index = (struct nvdimm_label_index *)((uint8_t *)index + size * offset); 189ad30b2f2SBen Widawsky if (strcmp(index->signature, NVDIMM_INDEX_BLOCK_SIGNATURE) != 0) 190ad30b2f2SBen Widawsky return false; 191ad30b2f2SBen Widawsky checksum = index->checksum; 192ad30b2f2SBen Widawsky index->checksum = 0; 193ad30b2f2SBen Widawsky if (checksum != fletcher64(index, size) || 194ad30b2f2SBen Widawsky index->this_offset != size * offset || index->this_size != size || 195ad30b2f2SBen Widawsky index->other_offset != size * (offset == 0 ? 1 : 0) || 196ad30b2f2SBen Widawsky index->seq == 0 || index->seq > 3 || index->slot_cnt > max_labels || 197ad30b2f2SBen Widawsky index->label_size != 1) 198ad30b2f2SBen Widawsky return false; 199ad30b2f2SBen Widawsky return true; 200ad30b2f2SBen Widawsky } 201ad30b2f2SBen Widawsky 202ad30b2f2SBen Widawsky static int 203ad30b2f2SBen Widawsky read_label(struct nvdimm_dev *nv, int num) 204ad30b2f2SBen Widawsky { 205ad30b2f2SBen Widawsky struct nvdimm_label_entry *entry, *i, *next; 206ad30b2f2SBen Widawsky uint64_t checksum; 207ad30b2f2SBen Widawsky off_t offset; 208ad30b2f2SBen Widawsky int error; 209ad30b2f2SBen Widawsky 210ad30b2f2SBen Widawsky offset = nv->label_index->label_offset + 211ad30b2f2SBen Widawsky num * (128 << nv->label_index->label_size); 212ad30b2f2SBen Widawsky entry = malloc(sizeof(*entry), M_NVDIMM, M_WAITOK); 213ad30b2f2SBen Widawsky error = read_label_area(nv, (uint8_t *)&entry->label, offset, 214ad30b2f2SBen Widawsky sizeof(struct nvdimm_label)); 215ad30b2f2SBen Widawsky if (error != 0) { 216ad30b2f2SBen Widawsky free(entry, M_NVDIMM); 217ad30b2f2SBen Widawsky return (error); 218ad30b2f2SBen Widawsky } 219ad30b2f2SBen Widawsky checksum = entry->label.checksum; 220ad30b2f2SBen Widawsky entry->label.checksum = 0; 221ad30b2f2SBen Widawsky if (checksum != fletcher64(&entry->label, sizeof(entry->label)) || 222ad30b2f2SBen Widawsky entry->label.slot != num) { 223ad30b2f2SBen Widawsky free(entry, M_NVDIMM); 224ad30b2f2SBen Widawsky return (ENXIO); 225ad30b2f2SBen Widawsky } 226ad30b2f2SBen Widawsky 227ad30b2f2SBen Widawsky /* Insertion ordered by dimm_phys_addr */ 228ad30b2f2SBen Widawsky if (SLIST_EMPTY(&nv->labels) || 229ad30b2f2SBen Widawsky entry->label.dimm_phys_addr <= 230ad30b2f2SBen Widawsky SLIST_FIRST(&nv->labels)->label.dimm_phys_addr) { 231ad30b2f2SBen Widawsky SLIST_INSERT_HEAD(&nv->labels, entry, link); 232ad30b2f2SBen Widawsky return (0); 233ad30b2f2SBen Widawsky } 234ad30b2f2SBen Widawsky SLIST_FOREACH_SAFE(i, &nv->labels, link, next) { 235ad30b2f2SBen Widawsky if (next == NULL || 236ad30b2f2SBen Widawsky entry->label.dimm_phys_addr <= next->label.dimm_phys_addr) { 237ad30b2f2SBen Widawsky SLIST_INSERT_AFTER(i, entry, link); 238ad30b2f2SBen Widawsky return (0); 239ad30b2f2SBen Widawsky } 240ad30b2f2SBen Widawsky } 241c79cee71SKyle Evans __assert_unreachable(); 242ad30b2f2SBen Widawsky } 243ad30b2f2SBen Widawsky 244ad30b2f2SBen Widawsky static int 245ad30b2f2SBen Widawsky read_labels(struct nvdimm_dev *nv) 246ad30b2f2SBen Widawsky { 247178d6bc8SD Scott Phillips struct nvdimm_label_index *indices, *index1; 248395975eaSJohn Baldwin size_t index_size, num_labels; 249ad30b2f2SBen Widawsky int error, n; 250ad30b2f2SBen Widawsky bool index_0_valid, index_1_valid; 251ad30b2f2SBen Widawsky 252ad30b2f2SBen Widawsky for (index_size = 256; ; index_size += 256) { 253ad30b2f2SBen Widawsky num_labels = 8 * (index_size - 254ad30b2f2SBen Widawsky sizeof(struct nvdimm_label_index)); 255ad30b2f2SBen Widawsky if (index_size + num_labels * sizeof(struct nvdimm_label) >= 256ad30b2f2SBen Widawsky nv->label_area_size) 257ad30b2f2SBen Widawsky break; 258ad30b2f2SBen Widawsky } 259ad30b2f2SBen Widawsky num_labels = (nv->label_area_size - index_size) / 260ad30b2f2SBen Widawsky sizeof(struct nvdimm_label); 261ad30b2f2SBen Widawsky indices = malloc(2 * index_size, M_NVDIMM, M_WAITOK); 262178d6bc8SD Scott Phillips index1 = (void *)((uint8_t *)indices + index_size); 263ad30b2f2SBen Widawsky error = read_label_area(nv, (void *)indices, 0, 2 * index_size); 264ad30b2f2SBen Widawsky if (error != 0) { 265ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 266ad30b2f2SBen Widawsky return (error); 267ad30b2f2SBen Widawsky } 268ad30b2f2SBen Widawsky index_0_valid = label_index_is_valid(indices, num_labels, index_size, 269ad30b2f2SBen Widawsky 0); 270ad30b2f2SBen Widawsky index_1_valid = label_index_is_valid(indices, num_labels, index_size, 271ad30b2f2SBen Widawsky 1); 272ad30b2f2SBen Widawsky if (!index_0_valid && !index_1_valid) { 273ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 274ad30b2f2SBen Widawsky return (ENXIO); 275ad30b2f2SBen Widawsky } 276178d6bc8SD Scott Phillips if (index_0_valid && index_1_valid) { 277178d6bc8SD Scott Phillips if (((int)indices->seq - (int)index1->seq + 3) % 3 == 1) { 278178d6bc8SD Scott Phillips /* index 0 was more recently updated */ 279178d6bc8SD Scott Phillips index_1_valid = false; 280178d6bc8SD Scott Phillips } else { 281178d6bc8SD Scott Phillips /* 282178d6bc8SD Scott Phillips * either index 1 was more recently updated, 283178d6bc8SD Scott Phillips * or the sequence numbers are equal, in which 284178d6bc8SD Scott Phillips * case the specification says the block with 285178d6bc8SD Scott Phillips * the higher offset is to be treated as valid 286178d6bc8SD Scott Phillips */ 287ad30b2f2SBen Widawsky index_0_valid = false; 288178d6bc8SD Scott Phillips } 289178d6bc8SD Scott Phillips } 290ad30b2f2SBen Widawsky nv->label_index = malloc(index_size, M_NVDIMM, M_WAITOK); 291178d6bc8SD Scott Phillips bcopy(index_0_valid ? indices : index1, nv->label_index, index_size); 292ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 293178d6bc8SD Scott Phillips bit_ffc_at((bitstr_t *)nv->label_index->free, 0, 294178d6bc8SD Scott Phillips nv->label_index->slot_cnt, &n); 295178d6bc8SD Scott Phillips while (n >= 0) { 296ad30b2f2SBen Widawsky read_label(nv, n); 297178d6bc8SD Scott Phillips bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1, 298178d6bc8SD Scott Phillips nv->label_index->slot_cnt, &n); 299ad30b2f2SBen Widawsky } 300ad30b2f2SBen Widawsky return (0); 301ad30b2f2SBen Widawsky } 302ad30b2f2SBen Widawsky 3036db7f8e5SKonstantin Belousov static int 3046db7f8e5SKonstantin Belousov nvdimm_probe(device_t dev) 3056db7f8e5SKonstantin Belousov { 3066db7f8e5SKonstantin Belousov 3076db7f8e5SKonstantin Belousov return (BUS_PROBE_NOWILDCARD); 3086db7f8e5SKonstantin Belousov } 3096db7f8e5SKonstantin Belousov 3106db7f8e5SKonstantin Belousov static int 3116db7f8e5SKonstantin Belousov nvdimm_attach(device_t dev) 3126db7f8e5SKonstantin Belousov { 3136db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 314bdde49b7SRavi Pokala struct sysctl_ctx_list *ctx; 315bdde49b7SRavi Pokala struct sysctl_oid *oid; 316bdde49b7SRavi Pokala struct sysctl_oid_list *children; 317bdde49b7SRavi Pokala struct sbuf *sb; 3186db7f8e5SKonstantin Belousov ACPI_TABLE_NFIT *nfitbl; 3196db7f8e5SKonstantin Belousov ACPI_STATUS status; 320bdde49b7SRavi Pokala ACPI_NFIT_MEMORY_MAP **maps; 321bdde49b7SRavi Pokala int error, i, num_maps; 322bdde49b7SRavi Pokala uint16_t flags; 3236db7f8e5SKonstantin Belousov 3246db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 325bdde49b7SRavi Pokala ctx = device_get_sysctl_ctx(dev); 326bdde49b7SRavi Pokala oid = device_get_sysctl_tree(dev); 327bdde49b7SRavi Pokala children = SYSCTL_CHILDREN(oid); 328bf264886SJohn Baldwin MPASS(nvdimm_root_get_acpi_handle(dev) != NULL); 3296db7f8e5SKonstantin Belousov nv->nv_dev = dev; 330fc4a961aSKonstantin Belousov nv->nv_handle = nvdimm_root_get_device_handle(dev); 3316db7f8e5SKonstantin Belousov 3326db7f8e5SKonstantin Belousov status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); 3336db7f8e5SKonstantin Belousov if (ACPI_FAILURE(status)) { 3346db7f8e5SKonstantin Belousov if (bootverbose) 3356db7f8e5SKonstantin Belousov device_printf(dev, "cannot get NFIT\n"); 3366db7f8e5SKonstantin Belousov return (ENXIO); 3376db7f8e5SKonstantin Belousov } 3387674dce0SKonstantin Belousov acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr, 3397674dce0SKonstantin Belousov &nv->nv_flush_addr_cnt); 340bdde49b7SRavi Pokala 341bdde49b7SRavi Pokala /* 342bdde49b7SRavi Pokala * Each NVDIMM should have at least one memory map associated with it. 343bdde49b7SRavi Pokala * If any of the maps have one of the error flags set, reflect that in 344bdde49b7SRavi Pokala * the overall status. 345bdde49b7SRavi Pokala */ 346bdde49b7SRavi Pokala acpi_nfit_get_memory_maps_by_dimm(nfitbl, nv->nv_handle, &maps, 347bdde49b7SRavi Pokala &num_maps); 348bdde49b7SRavi Pokala if (num_maps == 0) { 349bdde49b7SRavi Pokala free(nv->nv_flush_addr, M_NVDIMM); 350bdde49b7SRavi Pokala free(maps, M_NVDIMM); 351bdde49b7SRavi Pokala device_printf(dev, "cannot find memory map\n"); 352bdde49b7SRavi Pokala return (ENXIO); 353bdde49b7SRavi Pokala } 354bdde49b7SRavi Pokala flags = 0; 355bdde49b7SRavi Pokala for (i = 0; i < num_maps; i++) { 356bdde49b7SRavi Pokala flags |= maps[i]->Flags; 357bdde49b7SRavi Pokala } 358bdde49b7SRavi Pokala free(maps, M_NVDIMM); 359bdde49b7SRavi Pokala 360bdde49b7SRavi Pokala /* sbuf_new_auto(9) is M_WAITOK; no need to check for NULL. */ 361bdde49b7SRavi Pokala sb = sbuf_new_auto(); 362bdde49b7SRavi Pokala (void) sbuf_printf(sb, "0x%b", flags, 363bdde49b7SRavi Pokala "\20" 364bdde49b7SRavi Pokala "\001SAVE_FAILED" 365bdde49b7SRavi Pokala "\002RESTORE_FAILED" 366bdde49b7SRavi Pokala "\003FLUSH_FAILED" 367bdde49b7SRavi Pokala "\004NOT_ARMED" 368bdde49b7SRavi Pokala "\005HEALTH_OBSERVED" 369bdde49b7SRavi Pokala "\006HEALTH_ENABLED" 370bdde49b7SRavi Pokala "\007MAP_FAILED"); 371bdde49b7SRavi Pokala error = sbuf_finish(sb); 372bdde49b7SRavi Pokala if (error != 0) { 373bdde49b7SRavi Pokala sbuf_delete(sb); 374bdde49b7SRavi Pokala free(nv->nv_flush_addr, M_NVDIMM); 375bdde49b7SRavi Pokala device_printf(dev, "cannot convert flags to string\n"); 376bdde49b7SRavi Pokala return (error); 377bdde49b7SRavi Pokala } 378bdde49b7SRavi Pokala /* strdup(9) is M_WAITOK; no need to check for NULL. */ 379bdde49b7SRavi Pokala nv->nv_flags_str = strdup(sbuf_data(sb), M_NVDIMM); 380bdde49b7SRavi Pokala sbuf_delete(sb); 381bdde49b7SRavi Pokala SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "flags", 382bdde49b7SRavi Pokala CTLFLAG_RD | CTLFLAG_MPSAFE, nv->nv_flags_str, 0, 383bdde49b7SRavi Pokala "NVDIMM State Flags"); 384bdde49b7SRavi Pokala /* 385bdde49b7SRavi Pokala * Anything other than HEALTH_ENABLED indicates a fault condition of 386bdde49b7SRavi Pokala * some kind, so log if that's seen. 387bdde49b7SRavi Pokala */ 388bdde49b7SRavi Pokala if ((flags & ~ACPI_NFIT_MEM_HEALTH_ENABLED) != 0) 389bdde49b7SRavi Pokala device_printf(dev, "flags: %s\n", nv->nv_flags_str); 390bdde49b7SRavi Pokala 3916db7f8e5SKonstantin Belousov AcpiPutTable(&nfitbl->Header); 392ad30b2f2SBen Widawsky error = read_label_area_size(nv); 393ad30b2f2SBen Widawsky if (error == 0) { 394ad30b2f2SBen Widawsky /* 395ad30b2f2SBen Widawsky * Ignoring errors reading labels. Not all NVDIMMs 396ad30b2f2SBen Widawsky * support labels and namespaces. 397ad30b2f2SBen Widawsky */ 398ad30b2f2SBen Widawsky read_labels(nv); 399ad30b2f2SBen Widawsky } 4006db7f8e5SKonstantin Belousov return (0); 4016db7f8e5SKonstantin Belousov } 4026db7f8e5SKonstantin Belousov 4036db7f8e5SKonstantin Belousov static int 4046db7f8e5SKonstantin Belousov nvdimm_detach(device_t dev) 4056db7f8e5SKonstantin Belousov { 4066db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 407ad30b2f2SBen Widawsky struct nvdimm_label_entry *label, *next; 4086db7f8e5SKonstantin Belousov 4096db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 410bdde49b7SRavi Pokala free(nv->nv_flags_str, M_NVDIMM); 4116db7f8e5SKonstantin Belousov free(nv->nv_flush_addr, M_NVDIMM); 412ad30b2f2SBen Widawsky free(nv->label_index, M_NVDIMM); 413ad30b2f2SBen Widawsky SLIST_FOREACH_SAFE(label, &nv->labels, link, next) { 414ad30b2f2SBen Widawsky SLIST_REMOVE_HEAD(&nv->labels, link); 415ad30b2f2SBen Widawsky free(label, M_NVDIMM); 416ad30b2f2SBen Widawsky } 4176db7f8e5SKonstantin Belousov return (0); 4186db7f8e5SKonstantin Belousov } 4196db7f8e5SKonstantin Belousov 4206db7f8e5SKonstantin Belousov static int 4216db7f8e5SKonstantin Belousov nvdimm_suspend(device_t dev) 4226db7f8e5SKonstantin Belousov { 4236db7f8e5SKonstantin Belousov 4246db7f8e5SKonstantin Belousov return (0); 4256db7f8e5SKonstantin Belousov } 4266db7f8e5SKonstantin Belousov 4276db7f8e5SKonstantin Belousov static int 4286db7f8e5SKonstantin Belousov nvdimm_resume(device_t dev) 4296db7f8e5SKonstantin Belousov { 4306db7f8e5SKonstantin Belousov 4316db7f8e5SKonstantin Belousov return (0); 4326db7f8e5SKonstantin Belousov } 4336db7f8e5SKonstantin Belousov 4346db7f8e5SKonstantin Belousov static device_method_t nvdimm_methods[] = { 4356db7f8e5SKonstantin Belousov DEVMETHOD(device_probe, nvdimm_probe), 4366db7f8e5SKonstantin Belousov DEVMETHOD(device_attach, nvdimm_attach), 4376db7f8e5SKonstantin Belousov DEVMETHOD(device_detach, nvdimm_detach), 4386db7f8e5SKonstantin Belousov DEVMETHOD(device_suspend, nvdimm_suspend), 4396db7f8e5SKonstantin Belousov DEVMETHOD(device_resume, nvdimm_resume), 4406db7f8e5SKonstantin Belousov DEVMETHOD_END 4416db7f8e5SKonstantin Belousov }; 4426db7f8e5SKonstantin Belousov 4436db7f8e5SKonstantin Belousov static driver_t nvdimm_driver = { 4446db7f8e5SKonstantin Belousov "nvdimm", 4456db7f8e5SKonstantin Belousov nvdimm_methods, 4466db7f8e5SKonstantin Belousov sizeof(struct nvdimm_dev), 4476db7f8e5SKonstantin Belousov }; 4486db7f8e5SKonstantin Belousov 44945dc8e3cSJohn Baldwin struct nvdimm_dev * 45045dc8e3cSJohn Baldwin nvdimm_find_by_handle(nfit_handle_t nv_handle) 45145dc8e3cSJohn Baldwin { 45245dc8e3cSJohn Baldwin struct nvdimm_dev *res; 45345dc8e3cSJohn Baldwin device_t *dimms; 45445dc8e3cSJohn Baldwin int i, error, num_dimms; 45545dc8e3cSJohn Baldwin 45645dc8e3cSJohn Baldwin res = NULL; 45745dc8e3cSJohn Baldwin error = devclass_get_devices(devclass_find(nvdimm_driver.name), &dimms, 45845dc8e3cSJohn Baldwin &num_dimms); 45945dc8e3cSJohn Baldwin if (error != 0) 46045dc8e3cSJohn Baldwin return (NULL); 46145dc8e3cSJohn Baldwin for (i = 0; i < num_dimms; i++) { 46245dc8e3cSJohn Baldwin if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) { 46345dc8e3cSJohn Baldwin res = device_get_softc(dimms[i]); 46445dc8e3cSJohn Baldwin break; 46545dc8e3cSJohn Baldwin } 46645dc8e3cSJohn Baldwin } 46745dc8e3cSJohn Baldwin free(dimms, M_TEMP); 46845dc8e3cSJohn Baldwin return (res); 46945dc8e3cSJohn Baldwin } 47045dc8e3cSJohn Baldwin 471*534c3629SJohn Baldwin DRIVER_MODULE(nvdimm, nvdimm_acpi_root, nvdimm_driver, NULL, NULL); 4726db7f8e5SKonstantin Belousov MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); 473