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; 249ad30b2f2SBen Widawsky size_t bitfield_size, 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 bitfield_size = roundup2(num_labels, 8) / 8; 263ad30b2f2SBen Widawsky indices = malloc(2 * index_size, M_NVDIMM, M_WAITOK); 264178d6bc8SD Scott Phillips index1 = (void *)((uint8_t *)indices + index_size); 265ad30b2f2SBen Widawsky error = read_label_area(nv, (void *)indices, 0, 2 * index_size); 266ad30b2f2SBen Widawsky if (error != 0) { 267ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 268ad30b2f2SBen Widawsky return (error); 269ad30b2f2SBen Widawsky } 270ad30b2f2SBen Widawsky index_0_valid = label_index_is_valid(indices, num_labels, index_size, 271ad30b2f2SBen Widawsky 0); 272ad30b2f2SBen Widawsky index_1_valid = label_index_is_valid(indices, num_labels, index_size, 273ad30b2f2SBen Widawsky 1); 274ad30b2f2SBen Widawsky if (!index_0_valid && !index_1_valid) { 275ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 276ad30b2f2SBen Widawsky return (ENXIO); 277ad30b2f2SBen Widawsky } 278178d6bc8SD Scott Phillips if (index_0_valid && index_1_valid) { 279178d6bc8SD Scott Phillips if (((int)indices->seq - (int)index1->seq + 3) % 3 == 1) { 280178d6bc8SD Scott Phillips /* index 0 was more recently updated */ 281178d6bc8SD Scott Phillips index_1_valid = false; 282178d6bc8SD Scott Phillips } else { 283178d6bc8SD Scott Phillips /* 284178d6bc8SD Scott Phillips * either index 1 was more recently updated, 285178d6bc8SD Scott Phillips * or the sequence numbers are equal, in which 286178d6bc8SD Scott Phillips * case the specification says the block with 287178d6bc8SD Scott Phillips * the higher offset is to be treated as valid 288178d6bc8SD Scott Phillips */ 289ad30b2f2SBen Widawsky index_0_valid = false; 290178d6bc8SD Scott Phillips } 291178d6bc8SD Scott Phillips } 292ad30b2f2SBen Widawsky nv->label_index = malloc(index_size, M_NVDIMM, M_WAITOK); 293178d6bc8SD Scott Phillips bcopy(index_0_valid ? indices : index1, nv->label_index, index_size); 294ad30b2f2SBen Widawsky free(indices, M_NVDIMM); 295178d6bc8SD Scott Phillips bit_ffc_at((bitstr_t *)nv->label_index->free, 0, 296178d6bc8SD Scott Phillips nv->label_index->slot_cnt, &n); 297178d6bc8SD Scott Phillips while (n >= 0) { 298ad30b2f2SBen Widawsky read_label(nv, n); 299178d6bc8SD Scott Phillips bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1, 300178d6bc8SD Scott Phillips nv->label_index->slot_cnt, &n); 301ad30b2f2SBen Widawsky } 302ad30b2f2SBen Widawsky return (0); 303ad30b2f2SBen Widawsky } 304ad30b2f2SBen Widawsky 3056db7f8e5SKonstantin Belousov struct nvdimm_dev * 3066db7f8e5SKonstantin Belousov nvdimm_find_by_handle(nfit_handle_t nv_handle) 3076db7f8e5SKonstantin Belousov { 308fc4a961aSKonstantin Belousov struct nvdimm_dev *res; 309fc4a961aSKonstantin Belousov device_t *dimms; 310fc4a961aSKonstantin Belousov int i, error, num_dimms; 3116db7f8e5SKonstantin Belousov 3126db7f8e5SKonstantin Belousov res = NULL; 313fc4a961aSKonstantin Belousov error = devclass_get_devices(nvdimm_devclass, &dimms, &num_dimms); 314fc4a961aSKonstantin Belousov if (error != 0) 315fc4a961aSKonstantin Belousov return (NULL); 316fc4a961aSKonstantin Belousov for (i = 0; i < num_dimms; i++) { 317fc4a961aSKonstantin Belousov if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) { 318fc4a961aSKonstantin Belousov res = device_get_softc(dimms[i]); 3196db7f8e5SKonstantin Belousov break; 3206db7f8e5SKonstantin Belousov } 3216db7f8e5SKonstantin Belousov } 322fc4a961aSKonstantin Belousov free(dimms, M_TEMP); 3236db7f8e5SKonstantin Belousov return (res); 3246db7f8e5SKonstantin Belousov } 3256db7f8e5SKonstantin Belousov 3266db7f8e5SKonstantin Belousov static int 3276db7f8e5SKonstantin Belousov nvdimm_probe(device_t dev) 3286db7f8e5SKonstantin Belousov { 3296db7f8e5SKonstantin Belousov 3306db7f8e5SKonstantin Belousov return (BUS_PROBE_NOWILDCARD); 3316db7f8e5SKonstantin Belousov } 3326db7f8e5SKonstantin Belousov 3336db7f8e5SKonstantin Belousov static int 3346db7f8e5SKonstantin Belousov nvdimm_attach(device_t dev) 3356db7f8e5SKonstantin Belousov { 3366db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 337bdde49b7SRavi Pokala struct sysctl_ctx_list *ctx; 338bdde49b7SRavi Pokala struct sysctl_oid *oid; 339bdde49b7SRavi Pokala struct sysctl_oid_list *children; 340bdde49b7SRavi Pokala struct sbuf *sb; 3416db7f8e5SKonstantin Belousov ACPI_TABLE_NFIT *nfitbl; 3426db7f8e5SKonstantin Belousov ACPI_HANDLE handle; 3436db7f8e5SKonstantin Belousov ACPI_STATUS status; 344bdde49b7SRavi Pokala ACPI_NFIT_MEMORY_MAP **maps; 345bdde49b7SRavi Pokala int error, i, num_maps; 346bdde49b7SRavi Pokala uint16_t flags; 3476db7f8e5SKonstantin Belousov 3486db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 349bdde49b7SRavi Pokala ctx = device_get_sysctl_ctx(dev); 350bdde49b7SRavi Pokala oid = device_get_sysctl_tree(dev); 351bdde49b7SRavi Pokala children = SYSCTL_CHILDREN(oid); 352fc4a961aSKonstantin Belousov handle = nvdimm_root_get_acpi_handle(dev); 353963c89ffSConrad Meyer MPASS(handle != NULL); 3546db7f8e5SKonstantin Belousov nv->nv_dev = dev; 355fc4a961aSKonstantin Belousov nv->nv_handle = nvdimm_root_get_device_handle(dev); 3566db7f8e5SKonstantin Belousov 3576db7f8e5SKonstantin Belousov status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); 3586db7f8e5SKonstantin Belousov if (ACPI_FAILURE(status)) { 3596db7f8e5SKonstantin Belousov if (bootverbose) 3606db7f8e5SKonstantin Belousov device_printf(dev, "cannot get NFIT\n"); 3616db7f8e5SKonstantin Belousov return (ENXIO); 3626db7f8e5SKonstantin Belousov } 3637674dce0SKonstantin Belousov acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr, 3647674dce0SKonstantin Belousov &nv->nv_flush_addr_cnt); 365bdde49b7SRavi Pokala 366bdde49b7SRavi Pokala /* 367bdde49b7SRavi Pokala * Each NVDIMM should have at least one memory map associated with it. 368bdde49b7SRavi Pokala * If any of the maps have one of the error flags set, reflect that in 369bdde49b7SRavi Pokala * the overall status. 370bdde49b7SRavi Pokala */ 371bdde49b7SRavi Pokala acpi_nfit_get_memory_maps_by_dimm(nfitbl, nv->nv_handle, &maps, 372bdde49b7SRavi Pokala &num_maps); 373bdde49b7SRavi Pokala if (num_maps == 0) { 374bdde49b7SRavi Pokala free(nv->nv_flush_addr, M_NVDIMM); 375bdde49b7SRavi Pokala free(maps, M_NVDIMM); 376bdde49b7SRavi Pokala device_printf(dev, "cannot find memory map\n"); 377bdde49b7SRavi Pokala return (ENXIO); 378bdde49b7SRavi Pokala } 379bdde49b7SRavi Pokala flags = 0; 380bdde49b7SRavi Pokala for (i = 0; i < num_maps; i++) { 381bdde49b7SRavi Pokala flags |= maps[i]->Flags; 382bdde49b7SRavi Pokala } 383bdde49b7SRavi Pokala free(maps, M_NVDIMM); 384bdde49b7SRavi Pokala 385bdde49b7SRavi Pokala /* sbuf_new_auto(9) is M_WAITOK; no need to check for NULL. */ 386bdde49b7SRavi Pokala sb = sbuf_new_auto(); 387bdde49b7SRavi Pokala (void) sbuf_printf(sb, "0x%b", flags, 388bdde49b7SRavi Pokala "\20" 389bdde49b7SRavi Pokala "\001SAVE_FAILED" 390bdde49b7SRavi Pokala "\002RESTORE_FAILED" 391bdde49b7SRavi Pokala "\003FLUSH_FAILED" 392bdde49b7SRavi Pokala "\004NOT_ARMED" 393bdde49b7SRavi Pokala "\005HEALTH_OBSERVED" 394bdde49b7SRavi Pokala "\006HEALTH_ENABLED" 395bdde49b7SRavi Pokala "\007MAP_FAILED"); 396bdde49b7SRavi Pokala error = sbuf_finish(sb); 397bdde49b7SRavi Pokala if (error != 0) { 398bdde49b7SRavi Pokala sbuf_delete(sb); 399bdde49b7SRavi Pokala free(nv->nv_flush_addr, M_NVDIMM); 400bdde49b7SRavi Pokala device_printf(dev, "cannot convert flags to string\n"); 401bdde49b7SRavi Pokala return (error); 402bdde49b7SRavi Pokala } 403bdde49b7SRavi Pokala /* strdup(9) is M_WAITOK; no need to check for NULL. */ 404bdde49b7SRavi Pokala nv->nv_flags_str = strdup(sbuf_data(sb), M_NVDIMM); 405bdde49b7SRavi Pokala sbuf_delete(sb); 406bdde49b7SRavi Pokala SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "flags", 407bdde49b7SRavi Pokala CTLFLAG_RD | CTLFLAG_MPSAFE, nv->nv_flags_str, 0, 408bdde49b7SRavi Pokala "NVDIMM State Flags"); 409bdde49b7SRavi Pokala /* 410bdde49b7SRavi Pokala * Anything other than HEALTH_ENABLED indicates a fault condition of 411bdde49b7SRavi Pokala * some kind, so log if that's seen. 412bdde49b7SRavi Pokala */ 413bdde49b7SRavi Pokala if ((flags & ~ACPI_NFIT_MEM_HEALTH_ENABLED) != 0) 414bdde49b7SRavi Pokala device_printf(dev, "flags: %s\n", nv->nv_flags_str); 415bdde49b7SRavi Pokala 4166db7f8e5SKonstantin Belousov AcpiPutTable(&nfitbl->Header); 417ad30b2f2SBen Widawsky error = read_label_area_size(nv); 418ad30b2f2SBen Widawsky if (error == 0) { 419ad30b2f2SBen Widawsky /* 420ad30b2f2SBen Widawsky * Ignoring errors reading labels. Not all NVDIMMs 421ad30b2f2SBen Widawsky * support labels and namespaces. 422ad30b2f2SBen Widawsky */ 423ad30b2f2SBen Widawsky read_labels(nv); 424ad30b2f2SBen Widawsky } 4256db7f8e5SKonstantin Belousov return (0); 4266db7f8e5SKonstantin Belousov } 4276db7f8e5SKonstantin Belousov 4286db7f8e5SKonstantin Belousov static int 4296db7f8e5SKonstantin Belousov nvdimm_detach(device_t dev) 4306db7f8e5SKonstantin Belousov { 4316db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 432ad30b2f2SBen Widawsky struct nvdimm_label_entry *label, *next; 4336db7f8e5SKonstantin Belousov 4346db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 435bdde49b7SRavi Pokala free(nv->nv_flags_str, M_NVDIMM); 4366db7f8e5SKonstantin Belousov free(nv->nv_flush_addr, M_NVDIMM); 437ad30b2f2SBen Widawsky free(nv->label_index, M_NVDIMM); 438ad30b2f2SBen Widawsky SLIST_FOREACH_SAFE(label, &nv->labels, link, next) { 439ad30b2f2SBen Widawsky SLIST_REMOVE_HEAD(&nv->labels, link); 440ad30b2f2SBen Widawsky free(label, M_NVDIMM); 441ad30b2f2SBen Widawsky } 4426db7f8e5SKonstantin Belousov return (0); 4436db7f8e5SKonstantin Belousov } 4446db7f8e5SKonstantin Belousov 4456db7f8e5SKonstantin Belousov static int 4466db7f8e5SKonstantin Belousov nvdimm_suspend(device_t dev) 4476db7f8e5SKonstantin Belousov { 4486db7f8e5SKonstantin Belousov 4496db7f8e5SKonstantin Belousov return (0); 4506db7f8e5SKonstantin Belousov } 4516db7f8e5SKonstantin Belousov 4526db7f8e5SKonstantin Belousov static int 4536db7f8e5SKonstantin Belousov nvdimm_resume(device_t dev) 4546db7f8e5SKonstantin Belousov { 4556db7f8e5SKonstantin Belousov 4566db7f8e5SKonstantin Belousov return (0); 4576db7f8e5SKonstantin Belousov } 4586db7f8e5SKonstantin Belousov 4596db7f8e5SKonstantin Belousov static device_method_t nvdimm_methods[] = { 4606db7f8e5SKonstantin Belousov DEVMETHOD(device_probe, nvdimm_probe), 4616db7f8e5SKonstantin Belousov DEVMETHOD(device_attach, nvdimm_attach), 4626db7f8e5SKonstantin Belousov DEVMETHOD(device_detach, nvdimm_detach), 4636db7f8e5SKonstantin Belousov DEVMETHOD(device_suspend, nvdimm_suspend), 4646db7f8e5SKonstantin Belousov DEVMETHOD(device_resume, nvdimm_resume), 4656db7f8e5SKonstantin Belousov DEVMETHOD_END 4666db7f8e5SKonstantin Belousov }; 4676db7f8e5SKonstantin Belousov 4686db7f8e5SKonstantin Belousov static driver_t nvdimm_driver = { 4696db7f8e5SKonstantin Belousov "nvdimm", 4706db7f8e5SKonstantin Belousov nvdimm_methods, 4716db7f8e5SKonstantin Belousov sizeof(struct nvdimm_dev), 4726db7f8e5SKonstantin Belousov }; 4736db7f8e5SKonstantin Belousov 474963c89ffSConrad Meyer DRIVER_MODULE(nvdimm, nvdimm_acpi_root, nvdimm_driver, nvdimm_devclass, NULL, 475fc4a961aSKonstantin Belousov NULL); 4766db7f8e5SKonstantin Belousov MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); 477