Commit 1522ac3ec95ff0230e7aa516f86b674fdf72866c

Authored by Russell King
Committed by Russell King
1 parent 305b07680f

[ARM] Fix virtual to physical translation macro corner cases

The current use of these macros works well when the conversion is
entirely linear.  In this case, we can be assured that the following
holds true:

	__va(p + s) - s = __va(p)

However, this is not always the case, especially when there is a
non-linear conversion (eg, when there is a 3.5GB hole in memory.)
In this case, if 's' is the size of the region (eg, PAGE_SIZE) and
'p' is the final page, the above is most definitely not true.

So, we must ensure that __va() and __pa() are only used with valid
kernel direct mapped RAM addresses.  This patch tweaks the code
to achieve this.

Tested-by: Charles Moschel <fred99@carolina.rr.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Showing 3 changed files with 14 additions and 10 deletions Side-by-side Diff

arch/arm/mm/dma-mapping.c
... ... @@ -490,26 +490,30 @@
490 490 */
491 491 void dma_cache_maint(const void *start, size_t size, int direction)
492 492 {
493   - const void *end = start + size;
  493 + void (*inner_op)(const void *, const void *);
  494 + void (*outer_op)(unsigned long, unsigned long);
494 495  
495   - BUG_ON(!virt_addr_valid(start) || !virt_addr_valid(end - 1));
  496 + BUG_ON(!virt_addr_valid(start) || !virt_addr_valid(start + size - 1));
496 497  
497 498 switch (direction) {
498 499 case DMA_FROM_DEVICE: /* invalidate only */
499   - dmac_inv_range(start, end);
500   - outer_inv_range(__pa(start), __pa(end));
  500 + inner_op = dmac_inv_range;
  501 + outer_op = outer_inv_range;
501 502 break;
502 503 case DMA_TO_DEVICE: /* writeback only */
503   - dmac_clean_range(start, end);
504   - outer_clean_range(__pa(start), __pa(end));
  504 + inner_op = dmac_clean_range;
  505 + outer_op = outer_clean_range;
505 506 break;
506 507 case DMA_BIDIRECTIONAL: /* writeback and invalidate */
507   - dmac_flush_range(start, end);
508   - outer_flush_range(__pa(start), __pa(end));
  508 + inner_op = dmac_flush_range;
  509 + outer_op = outer_flush_range;
509 510 break;
510 511 default:
511 512 BUG();
512 513 }
  514 +
  515 + inner_op(start, start + size);
  516 + outer_op(__pa(start), __pa(start) + size);
513 517 }
514 518 EXPORT_SYMBOL(dma_cache_maint);
515 519  
... ... @@ -382,7 +382,7 @@
382 382 for_each_node(node)
383 383 bootmem_free_node(node, mi);
384 384  
385   - high_memory = __va(memend_pfn << PAGE_SHIFT);
  385 + high_memory = __va((memend_pfn << PAGE_SHIFT) - 1) + 1;
386 386  
387 387 /*
388 388 * This doesn't seem to be used by the Linux memory manager any
... ... @@ -124,7 +124,7 @@
124 124 {
125 125 if (addr < PHYS_OFFSET)
126 126 return 0;
127   - if (addr + size > __pa(high_memory))
  127 + if (addr + size >= __pa(high_memory - 1))
128 128 return 0;
129 129  
130 130 return 1;