Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI/IORT: Do not blindly trust DMA masks from firmware

Address issue observed on real world system with suboptimal IORT table
where DMA masks of PCI devices would get set to 0 as result.

iort_dma_setup() would query the root complex'/named component IORT
entry for a DMA mask, and use that over the one the device has been
configured with earlier.

Ideally we want to use the minimum mask of what the IORT contains for
the root complex and what the device was configured with.

Fixes: 5ac65e8c8941 ("ACPI/IORT: Support address size limit for root complexes")
Signed-off-by: Moritz Fischer <mdf@kernel.org>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Link: https://lore.kernel.org/r/20210122012419.95010-1-mdf@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Moritz Fischer and committed by
Catalin Marinas
a1df829e 519ea6f1

+12 -2
+12 -2
drivers/acpi/arm64/iort.c
··· 1107 1107 1108 1108 ncomp = (struct acpi_iort_named_component *)node->node_data; 1109 1109 1110 + if (!ncomp->memory_address_limit) { 1111 + pr_warn(FW_BUG "Named component missing memory address limit\n"); 1112 + return -EINVAL; 1113 + } 1114 + 1110 1115 *size = ncomp->memory_address_limit >= 64 ? U64_MAX : 1111 1116 1ULL<<ncomp->memory_address_limit; 1112 1117 ··· 1130 1125 return -ENODEV; 1131 1126 1132 1127 rc = (struct acpi_iort_root_complex *)node->node_data; 1128 + 1129 + if (!rc->memory_address_limit) { 1130 + pr_warn(FW_BUG "Root complex missing memory address limit\n"); 1131 + return -EINVAL; 1132 + } 1133 1133 1134 1134 *size = rc->memory_address_limit >= 64 ? U64_MAX : 1135 1135 1ULL<<rc->memory_address_limit; ··· 1183 1173 end = dmaaddr + size - 1; 1184 1174 mask = DMA_BIT_MASK(ilog2(end) + 1); 1185 1175 dev->bus_dma_limit = end; 1186 - dev->coherent_dma_mask = mask; 1187 - *dev->dma_mask = mask; 1176 + dev->coherent_dma_mask = min(dev->coherent_dma_mask, mask); 1177 + *dev->dma_mask = min(*dev->dma_mask, mask); 1188 1178 } 1189 1179 1190 1180 *dma_addr = dmaaddr;