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

ACPI/IORT: work around num_ids ambiguity

The ID mapping table structure of the IORT table describes the size of
a range using a num_ids field carrying the number of IDs in the region
minus one. This has been misinterpreted in the past in the parsing code,
and firmware is known to have shipped where this results in an ambiguity,
where regions that should be adjacent have an overlap of one value.

So let's work around this by detecting this case specifically: when
resolving an ID translation, allow one that matches right at the end of
a multi-ID region to be superseded by a subsequent one.

To prevent potential regressions on broken firmware that happened to
work before, only take the subsequent match into account if it occurs
at the start of a mapping region.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Link: https://lore.kernel.org/r/20200501161014.5935-3-ardb@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Ard Biesheuvel and committed by
Will Deacon
539979b6 6d3b29d0

+34 -6
+34 -6
drivers/acpi/arm64/iort.c
··· 300 300 } 301 301 302 302 static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 303 - u32 *rid_out) 303 + u32 *rid_out, bool check_overlap) 304 304 { 305 305 /* Single mapping does not care for input id */ 306 306 if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { ··· 316 316 } 317 317 318 318 if (rid_in < map->input_base || 319 - (rid_in >= map->input_base + map->id_count)) 319 + (rid_in > map->input_base + map->id_count)) 320 320 return -ENXIO; 321 321 322 + if (check_overlap) { 323 + /* 324 + * We already found a mapping for this input ID at the end of 325 + * another region. If it coincides with the start of this 326 + * region, we assume the prior match was due to the off-by-1 327 + * issue mentioned below, and allow it to be superseded. 328 + * Otherwise, things are *really* broken, and we just disregard 329 + * duplicate matches entirely to retain compatibility. 330 + */ 331 + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", 332 + map, rid_in); 333 + if (rid_in != map->input_base) 334 + return -ENXIO; 335 + } 336 + 322 337 *rid_out = map->output_base + (rid_in - map->input_base); 338 + 339 + /* 340 + * Due to confusion regarding the meaning of the id_count field (which 341 + * carries the number of IDs *minus 1*), we may have to disregard this 342 + * match if it is at the end of the range, and overlaps with the start 343 + * of another one. 344 + */ 345 + if (map->id_count > 0 && rid_in == map->input_base + map->id_count) 346 + return -EAGAIN; 323 347 return 0; 324 348 } 325 349 ··· 428 404 /* Parse the ID mapping tree to find specified node type */ 429 405 while (node) { 430 406 struct acpi_iort_id_mapping *map; 431 - int i, index; 407 + int i, index, rc = 0; 408 + u32 out_ref = 0, map_id = id; 432 409 433 410 if (IORT_TYPE_MASK(node->type) & type_mask) { 434 411 if (id_out) ··· 463 438 if (i == index) 464 439 continue; 465 440 466 - if (!iort_id_map(map, node->type, id, &id)) 441 + rc = iort_id_map(map, node->type, map_id, &id, out_ref); 442 + if (!rc) 467 443 break; 444 + if (rc == -EAGAIN) 445 + out_ref = map->output_reference; 468 446 } 469 447 470 - if (i == node->mapping_count) 448 + if (i == node->mapping_count && !out_ref) 471 449 goto fail_map; 472 450 473 451 node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, 474 - map->output_reference); 452 + rc ? out_ref : map->output_reference); 475 453 } 476 454 477 455 fail_map: