The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/nvdimm/nvdimm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2017 The FreeBSD Foundation
    3  * All rights reserved.
    4  * Copyright (c) 2018, 2019 Intel Corporation
    5  *
    6  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
    7  * under sponsorship from the FreeBSD Foundation.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include "opt_acpi.h"
   35 #include "opt_ddb.h"
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/bio.h>
   40 #include <sys/bitstring.h>
   41 #include <sys/bus.h>
   42 #include <sys/kernel.h>
   43 #include <sys/lock.h>
   44 #include <sys/malloc.h>
   45 #include <sys/module.h>
   46 #include <sys/sbuf.h>
   47 #include <sys/sysctl.h>
   48 #include <sys/uuid.h>
   49 
   50 #include <contrib/dev/acpica/include/acpi.h>
   51 #include <contrib/dev/acpica/include/accommon.h>
   52 #include <contrib/dev/acpica/include/acuuid.h>
   53 #include <dev/acpica/acpivar.h>
   54 
   55 #include <dev/nvdimm/nvdimm_var.h>
   56 
   57 #define _COMPONENT      ACPI_OEM
   58 ACPI_MODULE_NAME("NVDIMM")
   59 
   60 static struct uuid intel_nvdimm_dsm_uuid =
   61     {0x4309AC30,0x0D11,0x11E4,0x91,0x91,{0x08,0x00,0x20,0x0C,0x9A,0x66}};
   62 #define INTEL_NVDIMM_DSM_REV 1
   63 #define INTEL_NVDIMM_DSM_GET_LABEL_SIZE 4
   64 #define INTEL_NVDIMM_DSM_GET_LABEL_DATA 5
   65 
   66 MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory");
   67 
   68 static int
   69 read_label_area_size(struct nvdimm_dev *nv)
   70 {
   71         ACPI_OBJECT *result_buffer;
   72         ACPI_HANDLE handle;
   73         ACPI_STATUS status;
   74         ACPI_BUFFER result;
   75         uint32_t *out;
   76         int error;
   77 
   78         handle = nvdimm_root_get_acpi_handle(nv->nv_dev);
   79         if (handle == NULL)
   80                 return (ENODEV);
   81         result.Length = ACPI_ALLOCATE_BUFFER;
   82         result.Pointer = NULL;
   83         status = acpi_EvaluateDSM(handle, (uint8_t *)&intel_nvdimm_dsm_uuid,
   84             INTEL_NVDIMM_DSM_REV, INTEL_NVDIMM_DSM_GET_LABEL_SIZE, NULL,
   85             &result);
   86         error = ENXIO;
   87         if (ACPI_SUCCESS(status) && result.Pointer != NULL &&
   88             result.Length >= sizeof(ACPI_OBJECT)) {
   89                 result_buffer = result.Pointer;
   90                 if (result_buffer->Type == ACPI_TYPE_BUFFER &&
   91                     result_buffer->Buffer.Length >= 12) {
   92                         out = (uint32_t *)result_buffer->Buffer.Pointer;
   93                         nv->label_area_size = out[1];
   94                         nv->max_label_xfer = out[2];
   95                         error = 0;
   96                 }
   97         }
   98         if (result.Pointer != NULL)
   99                 AcpiOsFree(result.Pointer);
  100         return (error);
  101 }
  102 
  103 static int
  104 read_label_area(struct nvdimm_dev *nv, uint8_t *dest, off_t offset,
  105     off_t length)
  106 {
  107         ACPI_BUFFER result;
  108         ACPI_HANDLE handle;
  109         ACPI_OBJECT params_pkg, params_buf, *result_buf;
  110         ACPI_STATUS status;
  111         uint32_t params[2];
  112         off_t to_read;
  113         int error;
  114 
  115         error = 0;
  116         handle = nvdimm_root_get_acpi_handle(nv->nv_dev);
  117         if (offset < 0 || length <= 0 ||
  118             offset + length > nv->label_area_size ||
  119             handle == NULL)
  120                 return (ENODEV);
  121         params_pkg.Type = ACPI_TYPE_PACKAGE;
  122         params_pkg.Package.Count = 1;
  123         params_pkg.Package.Elements = &params_buf;
  124         params_buf.Type = ACPI_TYPE_BUFFER;
  125         params_buf.Buffer.Length = sizeof(params);
  126         params_buf.Buffer.Pointer = (UINT8 *)params;
  127         while (length > 0) {
  128                 to_read = MIN(length, nv->max_label_xfer);
  129                 params[0] = offset;
  130                 params[1] = to_read;
  131                 result.Length = ACPI_ALLOCATE_BUFFER;
  132                 result.Pointer = NULL;
  133                 status = acpi_EvaluateDSM(handle,
  134                     (uint8_t *)&intel_nvdimm_dsm_uuid, INTEL_NVDIMM_DSM_REV,
  135                     INTEL_NVDIMM_DSM_GET_LABEL_DATA, &params_pkg, &result);
  136                 if (ACPI_FAILURE(status) ||
  137                     result.Length < sizeof(ACPI_OBJECT) ||
  138                     result.Pointer == NULL) {
  139                         error = ENXIO;
  140                         break;
  141                 }
  142                 result_buf = (ACPI_OBJECT *)result.Pointer;
  143                 if (result_buf->Type != ACPI_TYPE_BUFFER ||
  144                     result_buf->Buffer.Pointer == NULL ||
  145                     result_buf->Buffer.Length != 4 + to_read ||
  146                     ((uint16_t *)result_buf->Buffer.Pointer)[0] != 0) {
  147                         error = ENXIO;
  148                         break;
  149                 }
  150                 bcopy(result_buf->Buffer.Pointer + 4, dest, to_read);
  151                 dest += to_read;
  152                 offset += to_read;
  153                 length -= to_read;
  154                 if (result.Pointer != NULL) {
  155                         AcpiOsFree(result.Pointer);
  156                         result.Pointer = NULL;
  157                 }
  158         }
  159         if (result.Pointer != NULL)
  160                 AcpiOsFree(result.Pointer);
  161         return (error);
  162 }
  163 
  164 static uint64_t
  165 fletcher64(const void *data, size_t length)
  166 {
  167         size_t i;
  168         uint32_t a, b;
  169         const uint32_t *d;
  170 
  171         a = 0;
  172         b = 0;
  173         d = (const uint32_t *)data;
  174         length = length / sizeof(uint32_t);
  175         for (i = 0; i < length; i++) {
  176                 a += d[i];
  177                 b += a;
  178         }
  179         return ((uint64_t)b << 32 | a);
  180 }
  181 
  182 static bool
  183 label_index_is_valid(struct nvdimm_label_index *index, uint32_t max_labels,
  184     size_t size, size_t offset)
  185 {
  186         uint64_t checksum;
  187 
  188         index = (struct nvdimm_label_index *)((uint8_t *)index + size * offset);
  189         if (strcmp(index->signature, NVDIMM_INDEX_BLOCK_SIGNATURE) != 0)
  190                 return false;
  191         checksum = index->checksum;
  192         index->checksum = 0;
  193         if (checksum != fletcher64(index, size) ||
  194             index->this_offset != size * offset || index->this_size != size ||
  195             index->other_offset != size * (offset == 0 ? 1 : 0) ||
  196             index->seq == 0 || index->seq > 3 || index->slot_cnt > max_labels ||
  197             index->label_size != 1)
  198                 return false;
  199         return true;
  200 }
  201 
  202 static int
  203 read_label(struct nvdimm_dev *nv, int num)
  204 {
  205         struct nvdimm_label_entry *entry, *i, *next;
  206         uint64_t checksum;
  207         off_t offset;
  208         int error;
  209 
  210         offset = nv->label_index->label_offset +
  211             num * (128 << nv->label_index->label_size);
  212         entry = malloc(sizeof(*entry), M_NVDIMM, M_WAITOK);
  213         error = read_label_area(nv, (uint8_t *)&entry->label, offset,
  214             sizeof(struct nvdimm_label));
  215         if (error != 0) {
  216                 free(entry, M_NVDIMM);
  217                 return (error);
  218         }
  219         checksum = entry->label.checksum;
  220         entry->label.checksum = 0;
  221         if (checksum != fletcher64(&entry->label, sizeof(entry->label)) ||
  222             entry->label.slot != num) {
  223                 free(entry, M_NVDIMM);
  224                 return (ENXIO);
  225         }
  226 
  227         /* Insertion ordered by dimm_phys_addr */
  228         if (SLIST_EMPTY(&nv->labels) ||
  229             entry->label.dimm_phys_addr <=
  230             SLIST_FIRST(&nv->labels)->label.dimm_phys_addr) {
  231                 SLIST_INSERT_HEAD(&nv->labels, entry, link);
  232                 return (0);
  233         }
  234         SLIST_FOREACH_SAFE(i, &nv->labels, link, next) {
  235                 if (next == NULL ||
  236                     entry->label.dimm_phys_addr <= next->label.dimm_phys_addr) {
  237                         SLIST_INSERT_AFTER(i, entry, link);
  238                         return (0);
  239                 }
  240         }
  241         __assert_unreachable();
  242 }
  243 
  244 static int
  245 read_labels(struct nvdimm_dev *nv)
  246 {
  247         struct nvdimm_label_index *indices, *index1;
  248         size_t index_size, num_labels;
  249         int error, n;
  250         bool index_0_valid, index_1_valid;
  251 
  252         for (index_size = 256; ; index_size += 256) {
  253                 num_labels = 8 * (index_size -
  254                     sizeof(struct nvdimm_label_index));
  255                 if (index_size + num_labels * sizeof(struct nvdimm_label) >=
  256                     nv->label_area_size)
  257                         break;
  258         }
  259         num_labels = (nv->label_area_size - index_size) /
  260             sizeof(struct nvdimm_label);
  261         indices = malloc(2 * index_size, M_NVDIMM, M_WAITOK);
  262         index1 = (void *)((uint8_t *)indices + index_size);
  263         error = read_label_area(nv, (void *)indices, 0, 2 * index_size);
  264         if (error != 0) {
  265                 free(indices, M_NVDIMM);
  266                 return (error);
  267         }
  268         index_0_valid = label_index_is_valid(indices, num_labels, index_size,
  269             0);
  270         index_1_valid = label_index_is_valid(indices, num_labels, index_size,
  271             1);
  272         if (!index_0_valid && !index_1_valid) {
  273                 free(indices, M_NVDIMM);
  274                 return (ENXIO);
  275         }
  276         if (index_0_valid && index_1_valid) {
  277                 if (((int)indices->seq - (int)index1->seq + 3) % 3 == 1) {
  278                         /* index 0 was more recently updated */
  279                         index_1_valid = false;
  280                 } else {
  281                         /*
  282                          * either index 1 was more recently updated,
  283                          * or the sequence numbers are equal, in which
  284                          * case the specification says the block with
  285                          * the higher offset is to be treated as valid
  286                          */
  287                         index_0_valid = false;
  288                 }
  289         }
  290         nv->label_index = malloc(index_size, M_NVDIMM, M_WAITOK);
  291         bcopy(index_0_valid ? indices : index1, nv->label_index, index_size);
  292         free(indices, M_NVDIMM);
  293         bit_ffc_at((bitstr_t *)nv->label_index->free, 0,
  294             nv->label_index->slot_cnt, &n);
  295         while (n >= 0) {
  296                 read_label(nv, n);
  297                 bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1,
  298                     nv->label_index->slot_cnt, &n);
  299         }
  300         return (0);
  301 }
  302 
  303 static int
  304 nvdimm_probe(device_t dev)
  305 {
  306 
  307         return (BUS_PROBE_NOWILDCARD);
  308 }
  309 
  310 static int
  311 nvdimm_attach(device_t dev)
  312 {
  313         struct nvdimm_dev *nv;
  314         struct sysctl_ctx_list *ctx;
  315         struct sysctl_oid *oid;
  316         struct sysctl_oid_list *children;
  317         struct sbuf *sb;
  318         ACPI_TABLE_NFIT *nfitbl;
  319         ACPI_STATUS status;
  320         ACPI_NFIT_MEMORY_MAP **maps;
  321         int error, i, num_maps;
  322         uint16_t flags;
  323 
  324         nv = device_get_softc(dev);
  325         ctx = device_get_sysctl_ctx(dev);
  326         oid = device_get_sysctl_tree(dev);
  327         children = SYSCTL_CHILDREN(oid);
  328         MPASS(nvdimm_root_get_acpi_handle(dev) != NULL);
  329         nv->nv_dev = dev;
  330         nv->nv_handle = nvdimm_root_get_device_handle(dev);
  331 
  332         status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl);
  333         if (ACPI_FAILURE(status)) {
  334                 if (bootverbose)
  335                         device_printf(dev, "cannot get NFIT\n");
  336                 return (ENXIO);
  337         }
  338         acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr,
  339             &nv->nv_flush_addr_cnt);
  340 
  341         /*
  342          * Each NVDIMM should have at least one memory map associated with it.
  343          * If any of the maps have one of the error flags set, reflect that in
  344          * the overall status.
  345          */
  346         acpi_nfit_get_memory_maps_by_dimm(nfitbl, nv->nv_handle, &maps,
  347             &num_maps);
  348         if (num_maps == 0) {
  349                 free(nv->nv_flush_addr, M_NVDIMM);
  350                 free(maps, M_NVDIMM);
  351                 device_printf(dev, "cannot find memory map\n");
  352                 return (ENXIO);
  353         }
  354         flags = 0;
  355         for (i = 0; i < num_maps; i++) {
  356                 flags |= maps[i]->Flags;
  357         }
  358         free(maps, M_NVDIMM);
  359 
  360         /* sbuf_new_auto(9) is M_WAITOK; no need to check for NULL. */
  361         sb = sbuf_new_auto();
  362         (void) sbuf_printf(sb, "0x%b", flags,
  363             "\2"
  364             "\001SAVE_FAILED"
  365             "\002RESTORE_FAILED"
  366             "\003FLUSH_FAILED"
  367             "\004NOT_ARMED"
  368             "\005HEALTH_OBSERVED"
  369             "\006HEALTH_ENABLED"
  370             "\007MAP_FAILED");
  371         error = sbuf_finish(sb);
  372         if (error != 0) {
  373                 sbuf_delete(sb);
  374                 free(nv->nv_flush_addr, M_NVDIMM);
  375                 device_printf(dev, "cannot convert flags to string\n");
  376                 return (error);
  377         }
  378         /* strdup(9) is M_WAITOK; no need to check for NULL. */
  379         nv->nv_flags_str = strdup(sbuf_data(sb), M_NVDIMM);
  380         sbuf_delete(sb);
  381         SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "flags",
  382             CTLFLAG_RD | CTLFLAG_MPSAFE, nv->nv_flags_str, 0,
  383             "NVDIMM State Flags");
  384         /*
  385          * Anything other than HEALTH_ENABLED indicates a fault condition of
  386          * some kind, so log if that's seen.
  387          */
  388         if ((flags & ~ACPI_NFIT_MEM_HEALTH_ENABLED) != 0)
  389                 device_printf(dev, "flags: %s\n", nv->nv_flags_str);
  390 
  391         AcpiPutTable(&nfitbl->Header);
  392         error = read_label_area_size(nv);
  393         if (error == 0) {
  394                 /*
  395                  * Ignoring errors reading labels. Not all NVDIMMs
  396                  * support labels and namespaces.
  397                  */
  398                 read_labels(nv);
  399         }
  400         return (0);
  401 }
  402 
  403 static int
  404 nvdimm_detach(device_t dev)
  405 {
  406         struct nvdimm_dev *nv;
  407         struct nvdimm_label_entry *label, *next;
  408 
  409         nv = device_get_softc(dev);
  410         free(nv->nv_flags_str, M_NVDIMM);
  411         free(nv->nv_flush_addr, M_NVDIMM);
  412         free(nv->label_index, M_NVDIMM);
  413         SLIST_FOREACH_SAFE(label, &nv->labels, link, next) {
  414                 SLIST_REMOVE_HEAD(&nv->labels, link);
  415                 free(label, M_NVDIMM);
  416         }
  417         return (0);
  418 }
  419 
  420 static int
  421 nvdimm_suspend(device_t dev)
  422 {
  423 
  424         return (0);
  425 }
  426 
  427 static int
  428 nvdimm_resume(device_t dev)
  429 {
  430 
  431         return (0);
  432 }
  433 
  434 static device_method_t nvdimm_methods[] = {
  435         DEVMETHOD(device_probe, nvdimm_probe),
  436         DEVMETHOD(device_attach, nvdimm_attach),
  437         DEVMETHOD(device_detach, nvdimm_detach),
  438         DEVMETHOD(device_suspend, nvdimm_suspend),
  439         DEVMETHOD(device_resume, nvdimm_resume),
  440         DEVMETHOD_END
  441 };
  442 
  443 static driver_t nvdimm_driver = {
  444         "nvdimm",
  445         nvdimm_methods,
  446         sizeof(struct nvdimm_dev),
  447 };
  448 
  449 struct nvdimm_dev *
  450 nvdimm_find_by_handle(nfit_handle_t nv_handle)
  451 {
  452         struct nvdimm_dev *res;
  453         device_t *dimms;
  454         int i, error, num_dimms;
  455 
  456         res = NULL;
  457         error = devclass_get_devices(devclass_find(nvdimm_driver.name), &dimms,
  458             &num_dimms);
  459         if (error != 0)
  460                 return (NULL);
  461         for (i = 0; i < num_dimms; i++) {
  462                 if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) {
  463                         res = device_get_softc(dimms[i]);
  464                         break;
  465                 }
  466         }
  467         free(dimms, M_TEMP);
  468         return (res);
  469 }
  470 
  471 DRIVER_MODULE(nvdimm, nvdimm_acpi_root, nvdimm_driver, NULL, NULL);
  472 MODULE_DEPEND(nvdimm, acpi, 1, 1, 1);

Cache object: f4f30c99700e64d980706acf6ebf3754


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.