xref: /freebsd-src/sys/dev/nvdimm/nvdimm_nfit.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
17674dce0SKonstantin Belousov /*-
27674dce0SKonstantin Belousov  * Copyright (c) 2018 Intel Corporation
37674dce0SKonstantin Belousov  * All rights reserved.
47674dce0SKonstantin Belousov  *
57674dce0SKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
67674dce0SKonstantin Belousov  * modification, are permitted provided that the following conditions
77674dce0SKonstantin Belousov  * are met:
87674dce0SKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
97674dce0SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
107674dce0SKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
117674dce0SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
127674dce0SKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
137674dce0SKonstantin Belousov  *
147674dce0SKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
157674dce0SKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
167674dce0SKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
177674dce0SKonstantin Belousov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
187674dce0SKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
197674dce0SKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
207674dce0SKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
217674dce0SKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
227674dce0SKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
237674dce0SKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
247674dce0SKonstantin Belousov  * SUCH DAMAGE.
257674dce0SKonstantin Belousov  */
267674dce0SKonstantin Belousov 
277674dce0SKonstantin Belousov #include <sys/param.h>
287674dce0SKonstantin Belousov #include <sys/bio.h>
297674dce0SKonstantin Belousov #include <sys/bus.h>
307674dce0SKonstantin Belousov #include <sys/malloc.h>
317674dce0SKonstantin Belousov #include <sys/uuid.h>
327674dce0SKonstantin Belousov 
337674dce0SKonstantin Belousov #include <contrib/dev/acpica/include/acpi.h>
347674dce0SKonstantin Belousov #include <dev/acpica/acpivar.h>
357674dce0SKonstantin Belousov #include <dev/nvdimm/nvdimm_var.h>
367674dce0SKonstantin Belousov 
377674dce0SKonstantin Belousov static int
uint32_t_compare(const void * a,const void * b)387674dce0SKonstantin Belousov uint32_t_compare(const void *a, const void *b)
397674dce0SKonstantin Belousov {
407674dce0SKonstantin Belousov 
417674dce0SKonstantin Belousov 	return (*(const uint32_t *)a - *(const uint32_t *)b);
427674dce0SKonstantin Belousov }
437674dce0SKonstantin Belousov 
447674dce0SKonstantin Belousov static int
find_matches(ACPI_TABLE_NFIT * nfitbl,uint16_t type,uint16_t offset,uint64_t mask,uint64_t value,void ** ptrs,int ptrs_len)457674dce0SKonstantin Belousov find_matches(ACPI_TABLE_NFIT *nfitbl, uint16_t type, uint16_t offset,
467674dce0SKonstantin Belousov     uint64_t mask, uint64_t value, void **ptrs, int ptrs_len)
477674dce0SKonstantin Belousov {
487674dce0SKonstantin Belousov 	ACPI_NFIT_HEADER *h, *end;
497674dce0SKonstantin Belousov 	uint64_t val;
507674dce0SKonstantin Belousov 	size_t load_size;
517674dce0SKonstantin Belousov 	int count;
527674dce0SKonstantin Belousov 
537674dce0SKonstantin Belousov 	h = (ACPI_NFIT_HEADER *)(nfitbl + 1);
547674dce0SKonstantin Belousov 	end = (ACPI_NFIT_HEADER *)((char *)nfitbl +
557674dce0SKonstantin Belousov 	    nfitbl->Header.Length);
567674dce0SKonstantin Belousov 	load_size = roundup2(flsl(mask), 8) / 8;
577674dce0SKonstantin Belousov 	count = 0;
587674dce0SKonstantin Belousov 
597674dce0SKonstantin Belousov 	while (h < end) {
607674dce0SKonstantin Belousov 		if (h->Type == type) {
617674dce0SKonstantin Belousov 			bcopy((char *)h + offset, &val, load_size);
627674dce0SKonstantin Belousov 			val &= mask;
637674dce0SKonstantin Belousov 			if (val == value) {
647674dce0SKonstantin Belousov 				if (ptrs_len > 0) {
657674dce0SKonstantin Belousov 					ptrs[count] = h;
667674dce0SKonstantin Belousov 					ptrs_len--;
677674dce0SKonstantin Belousov 				}
687674dce0SKonstantin Belousov 				count++;
697674dce0SKonstantin Belousov 			}
707674dce0SKonstantin Belousov 		}
717674dce0SKonstantin Belousov 		if (h->Length == 0)
727674dce0SKonstantin Belousov 			break;
737674dce0SKonstantin Belousov 		h = (ACPI_NFIT_HEADER *)((char *)h + h->Length);
747674dce0SKonstantin Belousov 	}
757674dce0SKonstantin Belousov 	return (count);
767674dce0SKonstantin Belousov }
777674dce0SKonstantin Belousov 
787674dce0SKonstantin Belousov static void
malloc_find_matches(ACPI_TABLE_NFIT * nfitbl,uint16_t type,uint16_t offset,uint64_t mask,uint64_t value,void *** ptrs,int * ptrs_len)797674dce0SKonstantin Belousov malloc_find_matches(ACPI_TABLE_NFIT *nfitbl, uint16_t type, uint16_t offset,
807674dce0SKonstantin Belousov     uint64_t mask, uint64_t value, void ***ptrs, int *ptrs_len)
817674dce0SKonstantin Belousov {
827674dce0SKonstantin Belousov 	int count;
837674dce0SKonstantin Belousov 
847674dce0SKonstantin Belousov 	count = find_matches(nfitbl, type, offset, mask, value, NULL, 0);
857674dce0SKonstantin Belousov 	*ptrs_len = count;
867674dce0SKonstantin Belousov 	if (count == 0) {
877674dce0SKonstantin Belousov 		*ptrs = NULL;
887674dce0SKonstantin Belousov 		return;
897674dce0SKonstantin Belousov 	}
907674dce0SKonstantin Belousov 	*ptrs = mallocarray(count, sizeof(void *), M_NVDIMM, M_WAITOK);
917674dce0SKonstantin Belousov 	find_matches(nfitbl, type, offset, mask, value, *ptrs, *ptrs_len);
927674dce0SKonstantin Belousov }
937674dce0SKonstantin Belousov 
947674dce0SKonstantin Belousov void
acpi_nfit_get_dimm_ids(ACPI_TABLE_NFIT * nfitbl,nfit_handle_t ** listp,int * countp)957674dce0SKonstantin Belousov acpi_nfit_get_dimm_ids(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t **listp,
967674dce0SKonstantin Belousov     int *countp)
977674dce0SKonstantin Belousov {
987674dce0SKonstantin Belousov 	ACPI_NFIT_SYSTEM_ADDRESS **spas;
997674dce0SKonstantin Belousov 	ACPI_NFIT_MEMORY_MAP ***regions;
1007674dce0SKonstantin Belousov 	int i, j, k, maxids, num_spas, *region_counts;
1017674dce0SKonstantin Belousov 
1027674dce0SKonstantin Belousov 	acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas);
1037674dce0SKonstantin Belousov 	if (num_spas == 0) {
1047674dce0SKonstantin Belousov 		*listp = NULL;
1057674dce0SKonstantin Belousov 		*countp = 0;
1067674dce0SKonstantin Belousov 		return;
1077674dce0SKonstantin Belousov 	}
1087674dce0SKonstantin Belousov 	regions = mallocarray(num_spas, sizeof(uint16_t *), M_NVDIMM,
1097674dce0SKonstantin Belousov 	    M_WAITOK);
1107674dce0SKonstantin Belousov 	region_counts = mallocarray(num_spas, sizeof(int), M_NVDIMM, M_WAITOK);
1117674dce0SKonstantin Belousov 	for (i = 0; i < num_spas; i++) {
1127674dce0SKonstantin Belousov 		acpi_nfit_get_region_mappings_by_spa_range(nfitbl,
1137674dce0SKonstantin Belousov 		    spas[i]->RangeIndex, &regions[i], &region_counts[i]);
1147674dce0SKonstantin Belousov 	}
1157674dce0SKonstantin Belousov 	maxids = 0;
1167674dce0SKonstantin Belousov 	for (i = 0; i < num_spas; i++) {
1177674dce0SKonstantin Belousov 		maxids += region_counts[i];
1187674dce0SKonstantin Belousov 	}
1197674dce0SKonstantin Belousov 	*listp = mallocarray(maxids, sizeof(nfit_handle_t), M_NVDIMM, M_WAITOK);
1207674dce0SKonstantin Belousov 	k = 0;
1217674dce0SKonstantin Belousov 	for (i = 0; i < num_spas; i++) {
1227674dce0SKonstantin Belousov 		for (j = 0; j < region_counts[i]; j++)
1237674dce0SKonstantin Belousov 			(*listp)[k++] = regions[i][j]->DeviceHandle;
1247674dce0SKonstantin Belousov 	}
1257674dce0SKonstantin Belousov 	qsort((*listp), maxids, sizeof(uint32_t), uint32_t_compare);
1267674dce0SKonstantin Belousov 	i = 0;
1277674dce0SKonstantin Belousov 	for (j = 1; j < maxids; j++) {
1287674dce0SKonstantin Belousov 		if ((*listp)[i] != (*listp)[j])
1297674dce0SKonstantin Belousov 			(*listp)[++i] = (*listp)[j];
1307674dce0SKonstantin Belousov 	}
1317674dce0SKonstantin Belousov 	*countp = i + 1;
1327674dce0SKonstantin Belousov 	free(region_counts, M_NVDIMM);
1337674dce0SKonstantin Belousov 	for (i = 0; i < num_spas; i++)
1347674dce0SKonstantin Belousov 		free(regions[i], M_NVDIMM);
1357674dce0SKonstantin Belousov 	free(regions, M_NVDIMM);
1367674dce0SKonstantin Belousov 	free(spas, M_NVDIMM);
1377674dce0SKonstantin Belousov }
1387674dce0SKonstantin Belousov 
1397674dce0SKonstantin Belousov void
acpi_nfit_get_spa_range(ACPI_TABLE_NFIT * nfitbl,uint16_t range_index,ACPI_NFIT_SYSTEM_ADDRESS ** spa)1407674dce0SKonstantin Belousov acpi_nfit_get_spa_range(ACPI_TABLE_NFIT *nfitbl, uint16_t range_index,
1417674dce0SKonstantin Belousov     ACPI_NFIT_SYSTEM_ADDRESS **spa)
1427674dce0SKonstantin Belousov {
1437674dce0SKonstantin Belousov 
1447674dce0SKonstantin Belousov 	*spa = NULL;
1457674dce0SKonstantin Belousov 	find_matches(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS,
1467674dce0SKonstantin Belousov 	    offsetof(ACPI_NFIT_SYSTEM_ADDRESS, RangeIndex), UINT16_MAX,
1477674dce0SKonstantin Belousov 	    range_index, (void **)spa, 1);
1487674dce0SKonstantin Belousov }
1497674dce0SKonstantin Belousov 
1507674dce0SKonstantin Belousov void
acpi_nfit_get_spa_ranges(ACPI_TABLE_NFIT * nfitbl,ACPI_NFIT_SYSTEM_ADDRESS *** listp,int * countp)1517674dce0SKonstantin Belousov acpi_nfit_get_spa_ranges(ACPI_TABLE_NFIT *nfitbl,
1527674dce0SKonstantin Belousov     ACPI_NFIT_SYSTEM_ADDRESS ***listp, int *countp)
1537674dce0SKonstantin Belousov {
1547674dce0SKonstantin Belousov 
1557674dce0SKonstantin Belousov 	malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, 0, 0, 0,
1567674dce0SKonstantin Belousov 	    (void ***)listp, countp);
1577674dce0SKonstantin Belousov }
1587674dce0SKonstantin Belousov 
1597674dce0SKonstantin Belousov void
acpi_nfit_get_region_mappings_by_spa_range(ACPI_TABLE_NFIT * nfitbl,uint16_t spa_range_index,ACPI_NFIT_MEMORY_MAP *** listp,int * countp)1607674dce0SKonstantin Belousov acpi_nfit_get_region_mappings_by_spa_range(ACPI_TABLE_NFIT *nfitbl,
1617674dce0SKonstantin Belousov     uint16_t spa_range_index, ACPI_NFIT_MEMORY_MAP ***listp, int *countp)
1627674dce0SKonstantin Belousov {
1637674dce0SKonstantin Belousov 
1647674dce0SKonstantin Belousov 	malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_MEMORY_MAP,
1657674dce0SKonstantin Belousov 	    offsetof(ACPI_NFIT_MEMORY_MAP, RangeIndex), UINT16_MAX,
1667674dce0SKonstantin Belousov 	    spa_range_index, (void ***)listp, countp);
1677674dce0SKonstantin Belousov }
1687674dce0SKonstantin Belousov 
acpi_nfit_get_control_region(ACPI_TABLE_NFIT * nfitbl,uint16_t control_region_index,ACPI_NFIT_CONTROL_REGION ** out)1697674dce0SKonstantin Belousov void acpi_nfit_get_control_region(ACPI_TABLE_NFIT *nfitbl,
1707674dce0SKonstantin Belousov     uint16_t control_region_index, ACPI_NFIT_CONTROL_REGION **out)
1717674dce0SKonstantin Belousov {
1727674dce0SKonstantin Belousov 
1737674dce0SKonstantin Belousov 	*out = NULL;
1747674dce0SKonstantin Belousov 	find_matches(nfitbl, ACPI_NFIT_TYPE_CONTROL_REGION,
1757674dce0SKonstantin Belousov 	    offsetof(ACPI_NFIT_CONTROL_REGION, RegionIndex), UINT16_MAX,
1767674dce0SKonstantin Belousov 	    control_region_index, (void **)out, 1);
1777674dce0SKonstantin Belousov }
1787674dce0SKonstantin Belousov 
1797674dce0SKonstantin Belousov void
acpi_nfit_get_flush_addrs(ACPI_TABLE_NFIT * nfitbl,nfit_handle_t dimm,uint64_t *** listp,int * countp)1807674dce0SKonstantin Belousov acpi_nfit_get_flush_addrs(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm,
1817674dce0SKonstantin Belousov     uint64_t ***listp, int *countp)
1827674dce0SKonstantin Belousov {
1837674dce0SKonstantin Belousov 	ACPI_NFIT_FLUSH_ADDRESS *subtable;
1847674dce0SKonstantin Belousov 	int i;
1857674dce0SKonstantin Belousov 
1867674dce0SKonstantin Belousov 	subtable = NULL;
1877674dce0SKonstantin Belousov 	find_matches(nfitbl, ACPI_NFIT_TYPE_FLUSH_ADDRESS,
1887674dce0SKonstantin Belousov 	    offsetof(ACPI_NFIT_FLUSH_ADDRESS, DeviceHandle), UINT32_MAX,
1897674dce0SKonstantin Belousov 	    dimm, (void **)&subtable, 1);
1907674dce0SKonstantin Belousov 	if (subtable == NULL || subtable->HintCount == 0) {
1917674dce0SKonstantin Belousov 		*listp = NULL;
1927674dce0SKonstantin Belousov 		*countp = 0;
1937674dce0SKonstantin Belousov 		return;
1947674dce0SKonstantin Belousov 	}
1957674dce0SKonstantin Belousov 	*countp = subtable->HintCount;
1967674dce0SKonstantin Belousov 	*listp = mallocarray(subtable->HintCount, sizeof(uint64_t *), M_NVDIMM,
1977674dce0SKonstantin Belousov 	    M_WAITOK);
1987674dce0SKonstantin Belousov 	for (i = 0; i < subtable->HintCount; i++)
1997674dce0SKonstantin Belousov 		(*listp)[i] = (uint64_t *)(intptr_t)subtable->HintAddress[i];
2007674dce0SKonstantin Belousov }
201*bdde49b7SRavi Pokala 
202*bdde49b7SRavi Pokala void
acpi_nfit_get_memory_maps_by_dimm(ACPI_TABLE_NFIT * nfitbl,nfit_handle_t dimm,ACPI_NFIT_MEMORY_MAP *** listp,int * countp)203*bdde49b7SRavi Pokala acpi_nfit_get_memory_maps_by_dimm(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm,
204*bdde49b7SRavi Pokala     ACPI_NFIT_MEMORY_MAP ***listp, int *countp)
205*bdde49b7SRavi Pokala {
206*bdde49b7SRavi Pokala 
207*bdde49b7SRavi Pokala 	malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_MEMORY_MAP,
208*bdde49b7SRavi Pokala 	    offsetof(ACPI_NFIT_MEMORY_MAP, DeviceHandle), UINT32_MAX, dimm,
209*bdde49b7SRavi Pokala 	    (void ***)listp, countp);
210*bdde49b7SRavi Pokala }
211