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 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/bio.h> 32 #include <sys/bus.h> 33 #include <sys/malloc.h> 34 #include <sys/uuid.h> 35 36 #include <contrib/dev/acpica/include/acpi.h> 37 #include <dev/acpica/acpivar.h> 38 #include <dev/nvdimm/nvdimm_var.h> 39 40 static int 41 uint32_t_compare(const void *a, const void *b) 42 { 43 44 return (*(const uint32_t *)a - *(const uint32_t *)b); 45 } 46 47 static int 48 find_matches(ACPI_TABLE_NFIT *nfitbl, uint16_t type, uint16_t offset, 49 uint64_t mask, uint64_t value, void **ptrs, int ptrs_len) 50 { 51 ACPI_NFIT_HEADER *h, *end; 52 uint64_t val; 53 size_t load_size; 54 int count; 55 56 h = (ACPI_NFIT_HEADER *)(nfitbl + 1); 57 end = (ACPI_NFIT_HEADER *)((char *)nfitbl + 58 nfitbl->Header.Length); 59 load_size = roundup2(flsl(mask), 8) / 8; 60 count = 0; 61 62 while (h < end) { 63 if (h->Type == type) { 64 bcopy((char *)h + offset, &val, load_size); 65 val &= mask; 66 if (val == value) { 67 if (ptrs_len > 0) { 68 ptrs[count] = h; 69 ptrs_len--; 70 } 71 count++; 72 } 73 } 74 if (h->Length == 0) 75 break; 76 h = (ACPI_NFIT_HEADER *)((char *)h + h->Length); 77 } 78 return (count); 79 } 80 81 static void 82 malloc_find_matches(ACPI_TABLE_NFIT *nfitbl, uint16_t type, uint16_t offset, 83 uint64_t mask, uint64_t value, void ***ptrs, int *ptrs_len) 84 { 85 int count; 86 87 count = find_matches(nfitbl, type, offset, mask, value, NULL, 0); 88 *ptrs_len = count; 89 if (count == 0) { 90 *ptrs = NULL; 91 return; 92 } 93 *ptrs = mallocarray(count, sizeof(void *), M_NVDIMM, M_WAITOK); 94 find_matches(nfitbl, type, offset, mask, value, *ptrs, *ptrs_len); 95 } 96 97 void 98 acpi_nfit_get_dimm_ids(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t **listp, 99 int *countp) 100 { 101 ACPI_NFIT_SYSTEM_ADDRESS **spas; 102 ACPI_NFIT_MEMORY_MAP ***regions; 103 int i, j, k, maxids, num_spas, *region_counts; 104 105 acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas); 106 if (num_spas == 0) { 107 *listp = NULL; 108 *countp = 0; 109 return; 110 } 111 regions = mallocarray(num_spas, sizeof(uint16_t *), M_NVDIMM, 112 M_WAITOK); 113 region_counts = mallocarray(num_spas, sizeof(int), M_NVDIMM, M_WAITOK); 114 for (i = 0; i < num_spas; i++) { 115 acpi_nfit_get_region_mappings_by_spa_range(nfitbl, 116 spas[i]->RangeIndex, ®ions[i], ®ion_counts[i]); 117 } 118 maxids = 0; 119 for (i = 0; i < num_spas; i++) { 120 maxids += region_counts[i]; 121 } 122 *listp = mallocarray(maxids, sizeof(nfit_handle_t), M_NVDIMM, M_WAITOK); 123 k = 0; 124 for (i = 0; i < num_spas; i++) { 125 for (j = 0; j < region_counts[i]; j++) 126 (*listp)[k++] = regions[i][j]->DeviceHandle; 127 } 128 qsort((*listp), maxids, sizeof(uint32_t), uint32_t_compare); 129 i = 0; 130 for (j = 1; j < maxids; j++) { 131 if ((*listp)[i] != (*listp)[j]) 132 (*listp)[++i] = (*listp)[j]; 133 } 134 *countp = i + 1; 135 free(region_counts, M_NVDIMM); 136 for (i = 0; i < num_spas; i++) 137 free(regions[i], M_NVDIMM); 138 free(regions, M_NVDIMM); 139 free(spas, M_NVDIMM); 140 } 141 142 void 143 acpi_nfit_get_spa_range(ACPI_TABLE_NFIT *nfitbl, uint16_t range_index, 144 ACPI_NFIT_SYSTEM_ADDRESS **spa) 145 { 146 147 *spa = NULL; 148 find_matches(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, 149 offsetof(ACPI_NFIT_SYSTEM_ADDRESS, RangeIndex), UINT16_MAX, 150 range_index, (void **)spa, 1); 151 } 152 153 void 154 acpi_nfit_get_spa_ranges(ACPI_TABLE_NFIT *nfitbl, 155 ACPI_NFIT_SYSTEM_ADDRESS ***listp, int *countp) 156 { 157 158 malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, 0, 0, 0, 159 (void ***)listp, countp); 160 } 161 162 void 163 acpi_nfit_get_region_mappings_by_spa_range(ACPI_TABLE_NFIT *nfitbl, 164 uint16_t spa_range_index, ACPI_NFIT_MEMORY_MAP ***listp, int *countp) 165 { 166 167 malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_MEMORY_MAP, 168 offsetof(ACPI_NFIT_MEMORY_MAP, RangeIndex), UINT16_MAX, 169 spa_range_index, (void ***)listp, countp); 170 } 171 172 void acpi_nfit_get_control_region(ACPI_TABLE_NFIT *nfitbl, 173 uint16_t control_region_index, ACPI_NFIT_CONTROL_REGION **out) 174 { 175 176 *out = NULL; 177 find_matches(nfitbl, ACPI_NFIT_TYPE_CONTROL_REGION, 178 offsetof(ACPI_NFIT_CONTROL_REGION, RegionIndex), UINT16_MAX, 179 control_region_index, (void **)out, 1); 180 } 181 182 void 183 acpi_nfit_get_flush_addrs(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm, 184 uint64_t ***listp, int *countp) 185 { 186 ACPI_NFIT_FLUSH_ADDRESS *subtable; 187 int i; 188 189 subtable = NULL; 190 find_matches(nfitbl, ACPI_NFIT_TYPE_FLUSH_ADDRESS, 191 offsetof(ACPI_NFIT_FLUSH_ADDRESS, DeviceHandle), UINT32_MAX, 192 dimm, (void **)&subtable, 1); 193 if (subtable == NULL || subtable->HintCount == 0) { 194 *listp = NULL; 195 *countp = 0; 196 return; 197 } 198 *countp = subtable->HintCount; 199 *listp = mallocarray(subtable->HintCount, sizeof(uint64_t *), M_NVDIMM, 200 M_WAITOK); 201 for (i = 0; i < subtable->HintCount; i++) 202 (*listp)[i] = (uint64_t *)(intptr_t)subtable->HintAddress[i]; 203 } 204 205 void 206 acpi_nfit_get_memory_maps_by_dimm(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm, 207 ACPI_NFIT_MEMORY_MAP ***listp, int *countp) 208 { 209 210 malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_MEMORY_MAP, 211 offsetof(ACPI_NFIT_MEMORY_MAP, DeviceHandle), UINT32_MAX, dimm, 212 (void ***)listp, countp); 213 } 214