1 /*- 2 * Copyright (c) 2018 Intel Corporation 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include <sys/param.h> 29 #include <sys/bio.h> 30 #include <sys/bus.h> 31 #include <sys/malloc.h> 32 #include <sys/uuid.h> 33 34 #include <contrib/dev/acpica/include/acpi.h> 35 #include <dev/acpica/acpivar.h> 36 #include <dev/nvdimm/nvdimm_var.h> 37 38 static int 39 uint32_t_compare(const void *a, const void *b) 40 { 41 42 return (*(const uint32_t *)a - *(const uint32_t *)b); 43 } 44 45 static int 46 find_matches(ACPI_TABLE_NFIT *nfitbl, uint16_t type, uint16_t offset, 47 uint64_t mask, uint64_t value, void **ptrs, int ptrs_len) 48 { 49 ACPI_NFIT_HEADER *h, *end; 50 uint64_t val; 51 size_t load_size; 52 int count; 53 54 h = (ACPI_NFIT_HEADER *)(nfitbl + 1); 55 end = (ACPI_NFIT_HEADER *)((char *)nfitbl + 56 nfitbl->Header.Length); 57 load_size = roundup2(flsl(mask), 8) / 8; 58 count = 0; 59 60 while (h < end) { 61 if (h->Type == type) { 62 bcopy((char *)h + offset, &val, load_size); 63 val &= mask; 64 if (val == value) { 65 if (ptrs_len > 0) { 66 ptrs[count] = h; 67 ptrs_len--; 68 } 69 count++; 70 } 71 } 72 if (h->Length == 0) 73 break; 74 h = (ACPI_NFIT_HEADER *)((char *)h + h->Length); 75 } 76 return (count); 77 } 78 79 static void 80 malloc_find_matches(ACPI_TABLE_NFIT *nfitbl, uint16_t type, uint16_t offset, 81 uint64_t mask, uint64_t value, void ***ptrs, int *ptrs_len) 82 { 83 int count; 84 85 count = find_matches(nfitbl, type, offset, mask, value, NULL, 0); 86 *ptrs_len = count; 87 if (count == 0) { 88 *ptrs = NULL; 89 return; 90 } 91 *ptrs = mallocarray(count, sizeof(void *), M_NVDIMM, M_WAITOK); 92 find_matches(nfitbl, type, offset, mask, value, *ptrs, *ptrs_len); 93 } 94 95 void 96 acpi_nfit_get_dimm_ids(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t **listp, 97 int *countp) 98 { 99 ACPI_NFIT_SYSTEM_ADDRESS **spas; 100 ACPI_NFIT_MEMORY_MAP ***regions; 101 int i, j, k, maxids, num_spas, *region_counts; 102 103 acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas); 104 if (num_spas == 0) { 105 *listp = NULL; 106 *countp = 0; 107 return; 108 } 109 regions = mallocarray(num_spas, sizeof(uint16_t *), M_NVDIMM, 110 M_WAITOK); 111 region_counts = mallocarray(num_spas, sizeof(int), M_NVDIMM, M_WAITOK); 112 for (i = 0; i < num_spas; i++) { 113 acpi_nfit_get_region_mappings_by_spa_range(nfitbl, 114 spas[i]->RangeIndex, ®ions[i], ®ion_counts[i]); 115 } 116 maxids = 0; 117 for (i = 0; i < num_spas; i++) { 118 maxids += region_counts[i]; 119 } 120 *listp = mallocarray(maxids, sizeof(nfit_handle_t), M_NVDIMM, M_WAITOK); 121 k = 0; 122 for (i = 0; i < num_spas; i++) { 123 for (j = 0; j < region_counts[i]; j++) 124 (*listp)[k++] = regions[i][j]->DeviceHandle; 125 } 126 qsort((*listp), maxids, sizeof(uint32_t), uint32_t_compare); 127 i = 0; 128 for (j = 1; j < maxids; j++) { 129 if ((*listp)[i] != (*listp)[j]) 130 (*listp)[++i] = (*listp)[j]; 131 } 132 *countp = i + 1; 133 free(region_counts, M_NVDIMM); 134 for (i = 0; i < num_spas; i++) 135 free(regions[i], M_NVDIMM); 136 free(regions, M_NVDIMM); 137 free(spas, M_NVDIMM); 138 } 139 140 void 141 acpi_nfit_get_spa_range(ACPI_TABLE_NFIT *nfitbl, uint16_t range_index, 142 ACPI_NFIT_SYSTEM_ADDRESS **spa) 143 { 144 145 *spa = NULL; 146 find_matches(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, 147 offsetof(ACPI_NFIT_SYSTEM_ADDRESS, RangeIndex), UINT16_MAX, 148 range_index, (void **)spa, 1); 149 } 150 151 void 152 acpi_nfit_get_spa_ranges(ACPI_TABLE_NFIT *nfitbl, 153 ACPI_NFIT_SYSTEM_ADDRESS ***listp, int *countp) 154 { 155 156 malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, 0, 0, 0, 157 (void ***)listp, countp); 158 } 159 160 void 161 acpi_nfit_get_region_mappings_by_spa_range(ACPI_TABLE_NFIT *nfitbl, 162 uint16_t spa_range_index, ACPI_NFIT_MEMORY_MAP ***listp, int *countp) 163 { 164 165 malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_MEMORY_MAP, 166 offsetof(ACPI_NFIT_MEMORY_MAP, RangeIndex), UINT16_MAX, 167 spa_range_index, (void ***)listp, countp); 168 } 169 170 void acpi_nfit_get_control_region(ACPI_TABLE_NFIT *nfitbl, 171 uint16_t control_region_index, ACPI_NFIT_CONTROL_REGION **out) 172 { 173 174 *out = NULL; 175 find_matches(nfitbl, ACPI_NFIT_TYPE_CONTROL_REGION, 176 offsetof(ACPI_NFIT_CONTROL_REGION, RegionIndex), UINT16_MAX, 177 control_region_index, (void **)out, 1); 178 } 179 180 void 181 acpi_nfit_get_flush_addrs(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm, 182 uint64_t ***listp, int *countp) 183 { 184 ACPI_NFIT_FLUSH_ADDRESS *subtable; 185 int i; 186 187 subtable = NULL; 188 find_matches(nfitbl, ACPI_NFIT_TYPE_FLUSH_ADDRESS, 189 offsetof(ACPI_NFIT_FLUSH_ADDRESS, DeviceHandle), UINT32_MAX, 190 dimm, (void **)&subtable, 1); 191 if (subtable == NULL || subtable->HintCount == 0) { 192 *listp = NULL; 193 *countp = 0; 194 return; 195 } 196 *countp = subtable->HintCount; 197 *listp = mallocarray(subtable->HintCount, sizeof(uint64_t *), M_NVDIMM, 198 M_WAITOK); 199 for (i = 0; i < subtable->HintCount; i++) 200 (*listp)[i] = (uint64_t *)(intptr_t)subtable->HintAddress[i]; 201 } 202 203 void 204 acpi_nfit_get_memory_maps_by_dimm(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm, 205 ACPI_NFIT_MEMORY_MAP ***listp, int *countp) 206 { 207 208 malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_MEMORY_MAP, 209 offsetof(ACPI_NFIT_MEMORY_MAP, DeviceHandle), UINT32_MAX, dimm, 210 (void ***)listp, countp); 211 } 212