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 static devclass_t nvdimm_devclass; 676db7f8e5SKonstantin Belousov MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory"); 686db7f8e5SKonstantin Belousov 69ad30b2f2SBen Widawsky static int 70ad30b2f2SBen Widawsky read_label_area_size(struct nvdimm_dev *nv) 71ad30b2f2SBen Widawsky { 72ad30b2f2SBen Widawsky ACPI_OBJECT *result_buffer; 73ad30b2f2SBen Widawsky ACPI_HANDLE handle; 74ad30b2f2SBen Widawsky ACPI_STATUS status; 75ad30b2f2SBen Widawsky ACPI_BUFFER result; 76ad30b2f2SBen Widawsky uint32_t *out; 77ad30b2f2SBen Widawsky int error; 78ad30b2f2SBen Widawsky 79ad30b2f2SBen Widawsky handle = nvdimm_root_get_acpi_handle(nv->nv_dev); 80ad30b2f2SBen Widawsky if (handle == NULL) 81ad30b2f2SBen Widawsky return (ENODEV); 82ad30b2f2SBen Widawsky result.Length = ACPI_ALLOCATE_BUFFER; 83ad30b2f2SBen Widawsky result.Pointer = NULL; 84ad30b2f2SBen Widawsky status = acpi_EvaluateDSM(handle, (uint8_t *)&intel_nvdimm_dsm_uuid, 85ad30b2f2SBen Widawsky INTEL_NVDIMM_DSM_REV, INTEL_NVDIMM_DSM_GET_LABEL_SIZE, NULL, 86ad30b2f2SBen Widawsky &result); 87ad30b2f2SBen Widawsky error = ENXIO; 88ad30b2f2SBen Widawsky if (ACPI_SUCCESS(status) && result.Pointer != NULL && 89ad30b2f2SBen Widawsky result.Length >= sizeof(ACPI_OBJECT)) { 90ad30b2f2SBen Widawsky result_buffer = result.Pointer; 91ad30b2f2SBen Widawsky if (result_buffer->Type == ACPI_TYPE_BUFFER && 92ad30b2f2SBen Widawsky result_buffer->Buffer.Length >= 12) { 93ad30b2f2SBen Widawsky out = (uint32_t *)result_buffer->Buffer.Pointer; 94ad30b2f2SBen Widawsky nv->label_area_size = out[1]; 95ad30b2f2SBen Widawsky nv->max_label_xfer = out[2]; 96ad30b2f2SBen Widawsky error = 0; 97ad30b2f2SBen Widawsky } 98ad30b2f2SBen Widawsky } 99ad30b2f2SBen Widawsky if (result.Pointer != NULL) 100ad30b2f2SBen Widawsky AcpiOsFree(result.Pointer); 101ad30b2f2SBen Widawsky return (error); 102ad30b2f2SBen Widawsky } 103ad30b2f2SBen Widawsky 104ad30b2f2SBen Widawsky static int 105ad30b2f2SBen Widawsky read_label_area(struct nvdimm_dev *nv, uint8_t *dest, off_t offset, 106ad30b2f2SBen Widawsky off_t length) 107ad30b2f2SBen Widawsky { 108ad30b2f2SBen Widawsky ACPI_BUFFER result; 109ad30b2f2SBen Widawsky ACPI_HANDLE handle; 110ad30b2f2SBen Widawsky ACPI_OBJECT params_pkg, params_buf, *result_buf; 111ad30b2f2SBen Widawsky ACPI_STATUS status; 112ad30b2f2SBen Widawsky uint32_t params[2]; 113ad30b2f2SBen Widawsky off_t to_read; 114ad30b2f2SBen Widawsky int error; 115ad30b2f2SBen Widawsky 116ad30b2f2SBen Widawsky error = 0; 117ad30b2f2SBen Widawsky handle = nvdimm_root_get_acpi_handle(nv->nv_dev); 118ad30b2f2SBen Widawsky if (offset < 0 || length <= 0 || 119ad30b2f2SBen Widawsky offset + length > nv->label_area_size || 120ad30b2f2SBen Widawsky handle == NULL) 121ad30b2f2SBen Widawsky return (ENODEV); 122ad30b2f2SBen Widawsky params_pkg.Type = ACPI_TYPE_PACKAGE; 123ad30b2f2SBen Widawsky params_pkg.Package.Count = 1; 124ad30b2f2SBen Widawsky params_pkg.Package.Elements = ¶ms_buf; 125ad30b2f2SBen Widawsky params_buf.Type = ACPI_TYPE_BUFFER; 126ad30b2f2SBen Widawsky params_buf.Buffer.Length = sizeof(params); 127ad30b2f2SBen Widawsky params_buf.Buffer.Pointer = (UINT8 *)params; 128ad30b2f2SBen Widawsky while (length > 0) { 129ad30b2f2SBen Widawsky to_read = MIN(length, nv->max_label_xfer); 130ad30b2f2SBen Widawsky params[0] = offset; 131ad30b2f2SBen Widawsky params[1] = to_read; 132ad30b2f2SBen Widawsky result.Length = ACPI_ALLOCATE_BUFFER; 133ad30b2f2SBen Widawsky result.Pointer = NULL; 134ad30b2f2SBen Widawsky status = acpi_EvaluateDSM(handle, 135ad30b2f2SBen Widawsky (uint8_t *)&intel_nvdimm_dsm_uuid, INTEL_NVDIMM_DSM_REV, 136ad30b2f2SBen Widawsky INTEL_NVDIMM_DSM_GET_LABEL_DATA, ¶ms_pkg, &result); 137ad30b2f2SBen Widawsky if (ACPI_FAILURE(status) || 138ad30b2f2SBen Widawsky result.Length < sizeof(ACPI_OBJECT) || 139ad30b2f2SBen Widawsky result.Pointer == NULL) { 140ad30b2f2SBen Widawsky error = ENXIO; 141ad30b2f2SBen Widawsky break; 142ad30b2f2SBen Widawsky } 143ad30b2f2SBen Widawsky result_buf = (ACPI_OBJECT *)result.Pointer; 144ad30b2f2SBen Widawsky if (result_buf->Type != ACPI_TYPE_BUFFER || 145ad30b2f2SBen Widawsky result_buf->Buffer.Pointer == NULL || 146ad30b2f2SBen Widawsky result_buf->Buffer.Length != 4 + to_read || 147ad30b2f2SBen Widawsky ((uint16_t *)result_buf->Buffer.Pointer)[0] != 0) { 148ad30b2f2SBen Widawsky error = ENXIO; 149ad30b2f2SBen Widawsky break; 150ad30b2f2SBen Widawsky } 151ad30b2f2SBen Widawsky bcopy(result_buf->Buffer.Pointer + 4, dest, to_read); 152ad30b2f2SBen Widawsky dest += to_read; 153ad30b2f2SBen Widawsky offset += to_read; 154ad30b2f2SBen Widawsky length -= to_read; 155ad30b2f2SBen Widawsky if (result.Pointer != NULL) { 156ad30b2f2SBen Widawsky AcpiOsFree(result.Pointer); 157ad30b2f2SBen Widawsky result.Pointer = NULL; 158ad30b2f2SBen Widawsky } 159ad30b2f2SBen Widawsky } 160ad30b2f2SBen Widawsky if (result.Pointer != NULL) 161ad30b2f2SBen Widawsky AcpiOsFree(result.Pointer); 162ad30b2f2SBen Widawsky return (error); 163ad30b2f2SBen Widawsky } 164ad30b2f2SBen Widawsky 165ad30b2f2SBen Widawsky static uint64_t 166ad30b2f2SBen Widawsky fletcher64(const void *data, size_t length) 167ad30b2f2SBen Widawsky { 168ad30b2f2SBen Widawsky size_t i; 169ad30b2f2SBen Widawsky uint32_t a, b; 170ad30b2f2SBen Widawsky const uint32_t *d; 171ad30b2f2SBen Widawsky 172ad30b2f2SBen Widawsky a = 0; 173ad30b2f2SBen Widawsky b = 0; 174ad30b2f2SBen Widawsky d = (const uint32_t *)data; 175ad30b2f2SBen Widawsky length = length / sizeof(uint32_t); 176ad30b2f2SBen Widawsky for (i = 0; i < length; i++) { 177ad30b2f2SBen Widawsky a += d[i]; 178ad30b2f2SBen Widawsky b += a; 179ad30b2f2SBen Widawsky } 180ad30b2f2SBen Widawsky return ((uint64_t)b << 32 | a); 181ad30b2f2SBen Widawsky } 182ad30b2f2SBen Widawsky 183ad30b2f2SBen Widawsky static bool 184ad30b2f2SBen Widawsky label_index_is_valid(struct nvdimm_label_index *index, uint32_t max_labels, 185ad30b2f2SBen Widawsky size_t size, size_t offset) 186ad30b2f2SBen Widawsky { 187ad30b2f2SBen Widawsky uint64_t checksum; 188ad30b2f2SBen Widawsky 189178d6bc8SD Scott Phillips index = (struct nvdimm_label_index *)((uint8_t *)index + size * offset); 190ad30b2f2SBen Widawsky if (strcmp(index->signature, NVDIMM_INDEX_BLOCK_SIGNATURE) != 0) 191ad30b2f2SBen Widawsky return false; 192ad30b2f2SBen Widawsky checksum = index->checksum; 193ad30b2f2SBen Widawsky index->checksum = 0; 194ad30b2f2SBen Widawsky if (checksum != fletcher64(index, size) || 195ad30b2f2SBen Widawsky index->this_offset != size * offset || index->this_size != size || 196ad30b2f2SBen Widawsky index->other_offset != size * (offset == 0 ? 1 : 0) || 197ad30b2f2SBen Widawsky index->seq == 0 || index->seq > 3 || index->slot_cnt > max_labels || 198ad30b2f2SBen Widawsky index->label_size != 1) 199ad30b2f2SBen Widawsky return false; 200ad30b2f2SBen Widawsky return true; 201ad30b2f2SBen Widawsky } 202ad30b2f2SBen Widawsky 203ad30b2f2SBen Widawsky static int 204ad30b2f2SBen Widawsky read_label(struct nvdimm_dev *nv, int num) 205ad30b2f2SBen Widawsky { 206ad30b2f2SBen Widawsky struct nvdimm_label_entry *entry, *i, *next; 207ad30b2f2SBen Widawsky uint64_t checksum; 208ad30b2f2SBen Widawsky off_t offset; 209ad30b2f2SBen Widawsky int error; 210ad30b2f2SBen Widawsky 211ad30b2f2SBen Widawsky offset = nv->label_index->label_offset + 212ad30b2f2SBen Widawsky num * (128 << nv->label_index->label_size); 213ad30b2f2SBen Widawsky entry = malloc(sizeof(*entry), M_NVDIMM, M_WAITOK); 214ad30b2f2SBen Widawsky error = read_label_area(nv, (uint8_t *)&entry->label, offset, 215ad30b2f2SBen Widawsky sizeof(struct nvdimm_label)); 216ad30b2f2SBen Widawsky if (error != 0) { 217ad30b2f2SBen Widawsky free(entry, M_NVDIMM); 218ad30b2f2SBen Widawsky return (error); 219ad30b2f2SBen Widawsky } 220ad30b2f2SBen Widawsky checksum = entry->label.checksum; 221ad30b2f2SBen Widawsky entry->label.checksum = 0; 222ad30b2f2SBen Widawsky if (checksum != fletcher64(&entry->label, sizeof(entry->label)) || 223ad30b2f2SBen Widawsky entry->label.slot != num) { 224ad30b2f2SBen Widawsky free(entry, M_NVDIMM); 225ad30b2f2SBen Widawsky return (ENXIO); 226ad30b2f2SBen Widawsky } 227ad30b2f2SBen Widawsky 228ad30b2f2SBen Widawsky /* Insertion ordered by dimm_phys_addr */ 229ad30b2f2SBen Widawsky if (SLIST_EMPTY(&nv->labels) || 230ad30b2f2SBen Widawsky entry->label.dimm_phys_addr <= 231ad30b2f2SBen Widawsky SLIST_FIRST(&nv->labels)->label.dimm_phys_addr) { 232ad30b2f2SBen Widawsky SLIST_INSERT_HEAD(&nv->labels, entry, link); 233ad30b2f2SBen Widawsky return (0); 234ad30b2f2SBen Widawsky } 235ad30b2f2SBen Widawsky SLIST_FOREACH_SAFE(i, &nv->labels, link, next) { 236ad30b2f2SBen Widawsky if (next == NULL || 237ad30b2f2SBen Widawsky entry->label.dimm_phys_addr <= next->label.dimm_phys_addr) { 238ad30b2f2SBen Widawsky SLIST_INSERT_AFTER(i, entry, link); 239ad30b2f2SBen Widawsky return (0); 240ad30b2f2SBen Widawsky } 241ad30b2f2SBen Widawsky } 242c79cee71SKyle Evans __assert_unreachable(); 243ad30b2f2SBen Widawsky } 244ad30b2f2SBen Widawsky 245ad30b2f2SBen Widawsky static int 246ad30b2f2SBen Widawsky read_labels(struct nvdimm_dev *nv) 247ad30b2f2SBen Widawsky { 248178d6bc8SD Scott Phillips struct nvdimm_label_index *indices, *index1; 249395975eaSJohn Baldwin size_t index_size, num_labels; 250ad30b2f2SBen Widawsky int error, n; 251ad30b2f2SBen Widawsky bool index_0_valid, index_1_valid; 252ad30b2f2SBen Widawsky 253ad30b2f2SBen Widawsky for (index_size = 256; ; index_size += 256) { 254ad30b2f2SBen Widawsky num_labels = 8 * (index_size - 255ad30b2f2SBen Widawsky sizeof(struct nvdimm_label_index)); 256ad30b2f2SBen Widawsky if (index_size + num_labels * sizeof(struct nvdimm_label) >= 257ad30b2f2SBen Widawsky nv->label_area_size) 258ad30b2f2SBen Widawsky break; 259ad30b2f2SBen Widawsky } 260ad30b2f2SBen Widawsky num_labels = (nv->label_area_size - index_size) / 261ad30b2f2SBen Widawsky sizeof(struct nvdimm_label); 262ad30b2f2SBen Widawsky indices = malloc(2 * index_size, M_NVDIMM, M_WAITOK); 263178d6bc8SD Scott Phillips index1 = (void *)((uint8_t *)indices + index_size); 264ad30b2f2SBen Widawsky error = read_label_area(nv, (void *)indices, 0, 2 * index_size); 265ad30b2f2SBen Widawsky if (error != 0) { 266ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 267ad30b2f2SBen Widawsky return (error); 268ad30b2f2SBen Widawsky } 269ad30b2f2SBen Widawsky index_0_valid = label_index_is_valid(indices, num_labels, index_size, 270ad30b2f2SBen Widawsky 0); 271ad30b2f2SBen Widawsky index_1_valid = label_index_is_valid(indices, num_labels, index_size, 272ad30b2f2SBen Widawsky 1); 273ad30b2f2SBen Widawsky if (!index_0_valid && !index_1_valid) { 274ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 275ad30b2f2SBen Widawsky return (ENXIO); 276ad30b2f2SBen Widawsky } 277178d6bc8SD Scott Phillips if (index_0_valid && index_1_valid) { 278178d6bc8SD Scott Phillips if (((int)indices->seq - (int)index1->seq + 3) % 3 == 1) { 279178d6bc8SD Scott Phillips /* index 0 was more recently updated */ 280178d6bc8SD Scott Phillips index_1_valid = false; 281178d6bc8SD Scott Phillips } else { 282178d6bc8SD Scott Phillips /* 283178d6bc8SD Scott Phillips * either index 1 was more recently updated, 284178d6bc8SD Scott Phillips * or the sequence numbers are equal, in which 285178d6bc8SD Scott Phillips * case the specification says the block with 286178d6bc8SD Scott Phillips * the higher offset is to be treated as valid 287178d6bc8SD Scott Phillips */ 288ad30b2f2SBen Widawsky index_0_valid = false; 289178d6bc8SD Scott Phillips } 290178d6bc8SD Scott Phillips } 291ad30b2f2SBen Widawsky nv->label_index = malloc(index_size, M_NVDIMM, M_WAITOK); 292178d6bc8SD Scott Phillips bcopy(index_0_valid ? indices : index1, nv->label_index, index_size); 293ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 294178d6bc8SD Scott Phillips bit_ffc_at((bitstr_t *)nv->label_index->free, 0, 295178d6bc8SD Scott Phillips nv->label_index->slot_cnt, &n); 296178d6bc8SD Scott Phillips while (n >= 0) { 297ad30b2f2SBen Widawsky read_label(nv, n); 298178d6bc8SD Scott Phillips bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1, 299178d6bc8SD Scott Phillips nv->label_index->slot_cnt, &n); 300ad30b2f2SBen Widawsky } 301ad30b2f2SBen Widawsky return (0); 302ad30b2f2SBen Widawsky } 303ad30b2f2SBen Widawsky 3046db7f8e5SKonstantin Belousov struct nvdimm_dev * 3056db7f8e5SKonstantin Belousov nvdimm_find_by_handle(nfit_handle_t nv_handle) 3066db7f8e5SKonstantin Belousov { 307fc4a961aSKonstantin Belousov struct nvdimm_dev *res; 308fc4a961aSKonstantin Belousov device_t *dimms; 309fc4a961aSKonstantin Belousov int i, error, num_dimms; 3106db7f8e5SKonstantin Belousov 3116db7f8e5SKonstantin Belousov res = NULL; 312fc4a961aSKonstantin Belousov error = devclass_get_devices(nvdimm_devclass, &dimms, &num_dimms); 313fc4a961aSKonstantin Belousov if (error != 0) 314fc4a961aSKonstantin Belousov return (NULL); 315fc4a961aSKonstantin Belousov for (i = 0; i < num_dimms; i++) { 316fc4a961aSKonstantin Belousov if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) { 317fc4a961aSKonstantin Belousov res = device_get_softc(dimms[i]); 3186db7f8e5SKonstantin Belousov break; 3196db7f8e5SKonstantin Belousov } 3206db7f8e5SKonstantin Belousov } 321fc4a961aSKonstantin Belousov free(dimms, M_TEMP); 3226db7f8e5SKonstantin Belousov return (res); 3236db7f8e5SKonstantin Belousov } 3246db7f8e5SKonstantin Belousov 3256db7f8e5SKonstantin Belousov static int 3266db7f8e5SKonstantin Belousov nvdimm_probe(device_t dev) 3276db7f8e5SKonstantin Belousov { 3286db7f8e5SKonstantin Belousov 3296db7f8e5SKonstantin Belousov return (BUS_PROBE_NOWILDCARD); 3306db7f8e5SKonstantin Belousov } 3316db7f8e5SKonstantin Belousov 3326db7f8e5SKonstantin Belousov static int 3336db7f8e5SKonstantin Belousov nvdimm_attach(device_t dev) 3346db7f8e5SKonstantin Belousov { 3356db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 336bdde49b7SRavi Pokala struct sysctl_ctx_list *ctx; 337bdde49b7SRavi Pokala struct sysctl_oid *oid; 338bdde49b7SRavi Pokala struct sysctl_oid_list *children; 339bdde49b7SRavi Pokala struct sbuf *sb; 3406db7f8e5SKonstantin Belousov ACPI_TABLE_NFIT *nfitbl; 3416db7f8e5SKonstantin Belousov ACPI_STATUS status; 342bdde49b7SRavi Pokala ACPI_NFIT_MEMORY_MAP **maps; 343bdde49b7SRavi Pokala int error, i, num_maps; 344bdde49b7SRavi Pokala uint16_t flags; 3456db7f8e5SKonstantin Belousov 3466db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 347bdde49b7SRavi Pokala ctx = device_get_sysctl_ctx(dev); 348bdde49b7SRavi Pokala oid = device_get_sysctl_tree(dev); 349bdde49b7SRavi Pokala children = SYSCTL_CHILDREN(oid); 350bf264886SJohn Baldwin MPASS(nvdimm_root_get_acpi_handle(dev) != NULL); 3516db7f8e5SKonstantin Belousov nv->nv_dev = dev; 352fc4a961aSKonstantin Belousov nv->nv_handle = nvdimm_root_get_device_handle(dev); 3536db7f8e5SKonstantin Belousov 3546db7f8e5SKonstantin Belousov status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); 3556db7f8e5SKonstantin Belousov if (ACPI_FAILURE(status)) { 3566db7f8e5SKonstantin Belousov if (bootverbose) 3576db7f8e5SKonstantin Belousov device_printf(dev, "cannot get NFIT\n"); 3586db7f8e5SKonstantin Belousov return (ENXIO); 3596db7f8e5SKonstantin Belousov } 3607674dce0SKonstantin Belousov acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr, 3617674dce0SKonstantin Belousov &nv->nv_flush_addr_cnt); 362bdde49b7SRavi Pokala 363bdde49b7SRavi Pokala /* 364bdde49b7SRavi Pokala * Each NVDIMM should have at least one memory map associated with it. 365bdde49b7SRavi Pokala * If any of the maps have one of the error flags set, reflect that in 366bdde49b7SRavi Pokala * the overall status. 367bdde49b7SRavi Pokala */ 368bdde49b7SRavi Pokala acpi_nfit_get_memory_maps_by_dimm(nfitbl, nv->nv_handle, &maps, 369bdde49b7SRavi Pokala &num_maps); 370bdde49b7SRavi Pokala if (num_maps == 0) { 371bdde49b7SRavi Pokala free(nv->nv_flush_addr, M_NVDIMM); 372bdde49b7SRavi Pokala free(maps, M_NVDIMM); 373bdde49b7SRavi Pokala device_printf(dev, "cannot find memory map\n"); 374bdde49b7SRavi Pokala return (ENXIO); 375bdde49b7SRavi Pokala } 376bdde49b7SRavi Pokala flags = 0; 377bdde49b7SRavi Pokala for (i = 0; i < num_maps; i++) { 378bdde49b7SRavi Pokala flags |= maps[i]->Flags; 379bdde49b7SRavi Pokala } 380bdde49b7SRavi Pokala free(maps, M_NVDIMM); 381bdde49b7SRavi Pokala 382bdde49b7SRavi Pokala /* sbuf_new_auto(9) is M_WAITOK; no need to check for NULL. */ 383bdde49b7SRavi Pokala sb = sbuf_new_auto(); 384bdde49b7SRavi Pokala (void) sbuf_printf(sb, "0x%b", flags, 385bdde49b7SRavi Pokala "\20" 386bdde49b7SRavi Pokala "\001SAVE_FAILED" 387bdde49b7SRavi Pokala "\002RESTORE_FAILED" 388bdde49b7SRavi Pokala "\003FLUSH_FAILED" 389bdde49b7SRavi Pokala "\004NOT_ARMED" 390bdde49b7SRavi Pokala "\005HEALTH_OBSERVED" 391bdde49b7SRavi Pokala "\006HEALTH_ENABLED" 392bdde49b7SRavi Pokala "\007MAP_FAILED"); 393bdde49b7SRavi Pokala error = sbuf_finish(sb); 394bdde49b7SRavi Pokala if (error != 0) { 395bdde49b7SRavi Pokala sbuf_delete(sb); 396bdde49b7SRavi Pokala free(nv->nv_flush_addr, M_NVDIMM); 397bdde49b7SRavi Pokala device_printf(dev, "cannot convert flags to string\n"); 398bdde49b7SRavi Pokala return (error); 399bdde49b7SRavi Pokala } 400bdde49b7SRavi Pokala /* strdup(9) is M_WAITOK; no need to check for NULL. */ 401bdde49b7SRavi Pokala nv->nv_flags_str = strdup(sbuf_data(sb), M_NVDIMM); 402bdde49b7SRavi Pokala sbuf_delete(sb); 403bdde49b7SRavi Pokala SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "flags", 404bdde49b7SRavi Pokala CTLFLAG_RD | CTLFLAG_MPSAFE, nv->nv_flags_str, 0, 405bdde49b7SRavi Pokala "NVDIMM State Flags"); 406bdde49b7SRavi Pokala /* 407bdde49b7SRavi Pokala * Anything other than HEALTH_ENABLED indicates a fault condition of 408bdde49b7SRavi Pokala * some kind, so log if that's seen. 409bdde49b7SRavi Pokala */ 410bdde49b7SRavi Pokala if ((flags & ~ACPI_NFIT_MEM_HEALTH_ENABLED) != 0) 411bdde49b7SRavi Pokala device_printf(dev, "flags: %s\n", nv->nv_flags_str); 412bdde49b7SRavi Pokala 4136db7f8e5SKonstantin Belousov AcpiPutTable(&nfitbl->Header); 414ad30b2f2SBen Widawsky error = read_label_area_size(nv); 415ad30b2f2SBen Widawsky if (error == 0) { 416ad30b2f2SBen Widawsky /* 417ad30b2f2SBen Widawsky * Ignoring errors reading labels. Not all NVDIMMs 418ad30b2f2SBen Widawsky * support labels and namespaces. 419ad30b2f2SBen Widawsky */ 420ad30b2f2SBen Widawsky read_labels(nv); 421ad30b2f2SBen Widawsky } 4226db7f8e5SKonstantin Belousov return (0); 4236db7f8e5SKonstantin Belousov } 4246db7f8e5SKonstantin Belousov 4256db7f8e5SKonstantin Belousov static int 4266db7f8e5SKonstantin Belousov nvdimm_detach(device_t dev) 4276db7f8e5SKonstantin Belousov { 4286db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 429ad30b2f2SBen Widawsky struct nvdimm_label_entry *label, *next; 4306db7f8e5SKonstantin Belousov 4316db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 432bdde49b7SRavi Pokala free(nv->nv_flags_str, M_NVDIMM); 4336db7f8e5SKonstantin Belousov free(nv->nv_flush_addr, M_NVDIMM); 434ad30b2f2SBen Widawsky free(nv->label_index, M_NVDIMM); 435ad30b2f2SBen Widawsky SLIST_FOREACH_SAFE(label, &nv->labels, link, next) { 436ad30b2f2SBen Widawsky SLIST_REMOVE_HEAD(&nv->labels, link); 437ad30b2f2SBen Widawsky free(label, M_NVDIMM); 438ad30b2f2SBen Widawsky } 4396db7f8e5SKonstantin Belousov return (0); 4406db7f8e5SKonstantin Belousov } 4416db7f8e5SKonstantin Belousov 4426db7f8e5SKonstantin Belousov static int 4436db7f8e5SKonstantin Belousov nvdimm_suspend(device_t dev) 4446db7f8e5SKonstantin Belousov { 4456db7f8e5SKonstantin Belousov 4466db7f8e5SKonstantin Belousov return (0); 4476db7f8e5SKonstantin Belousov } 4486db7f8e5SKonstantin Belousov 4496db7f8e5SKonstantin Belousov static int 4506db7f8e5SKonstantin Belousov nvdimm_resume(device_t dev) 4516db7f8e5SKonstantin Belousov { 4526db7f8e5SKonstantin Belousov 4536db7f8e5SKonstantin Belousov return (0); 4546db7f8e5SKonstantin Belousov } 4556db7f8e5SKonstantin Belousov 4566db7f8e5SKonstantin Belousov static device_method_t nvdimm_methods[] = { 4576db7f8e5SKonstantin Belousov DEVMETHOD(device_probe, nvdimm_probe), 4586db7f8e5SKonstantin Belousov DEVMETHOD(device_attach, nvdimm_attach), 4596db7f8e5SKonstantin Belousov DEVMETHOD(device_detach, nvdimm_detach), 4606db7f8e5SKonstantin Belousov DEVMETHOD(device_suspend, nvdimm_suspend), 4616db7f8e5SKonstantin Belousov DEVMETHOD(device_resume, nvdimm_resume), 4626db7f8e5SKonstantin Belousov DEVMETHOD_END 4636db7f8e5SKonstantin Belousov }; 4646db7f8e5SKonstantin Belousov 4656db7f8e5SKonstantin Belousov static driver_t nvdimm_driver = { 4666db7f8e5SKonstantin Belousov "nvdimm", 4676db7f8e5SKonstantin Belousov nvdimm_methods, 4686db7f8e5SKonstantin Belousov sizeof(struct nvdimm_dev), 4696db7f8e5SKonstantin Belousov }; 4706db7f8e5SKonstantin Belousov 471963c89ffSConrad Meyer DRIVER_MODULE(nvdimm, nvdimm_acpi_root, nvdimm_driver, nvdimm_devclass, NULL, 472fc4a961aSKonstantin Belousov NULL); 4736db7f8e5SKonstantin Belousov MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); 474