Skip to content

Commit

Permalink
[ARM] Fix virtual to physical translation macro corner cases
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Russell King <[email protected]>
  • Loading branch information
Russell King authored and Russell King committed Mar 12, 2009
1 parent 305b076 commit 1522ac3
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 10 deletions.
20 changes: 12 additions & 8 deletions arch/arm/mm/dma-mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,26 +490,30 @@ core_initcall(consistent_init);
*/
void dma_cache_maint(const void *start, size_t size, int direction)
{
const void *end = start + size;
void (*inner_op)(const void *, const void *);
void (*outer_op)(unsigned long, unsigned long);

BUG_ON(!virt_addr_valid(start) || !virt_addr_valid(end - 1));
BUG_ON(!virt_addr_valid(start) || !virt_addr_valid(start + size - 1));

switch (direction) {
case DMA_FROM_DEVICE: /* invalidate only */
dmac_inv_range(start, end);
outer_inv_range(__pa(start), __pa(end));
inner_op = dmac_inv_range;
outer_op = outer_inv_range;
break;
case DMA_TO_DEVICE: /* writeback only */
dmac_clean_range(start, end);
outer_clean_range(__pa(start), __pa(end));
inner_op = dmac_clean_range;
outer_op = outer_clean_range;
break;
case DMA_BIDIRECTIONAL: /* writeback and invalidate */
dmac_flush_range(start, end);
outer_flush_range(__pa(start), __pa(end));
inner_op = dmac_flush_range;
outer_op = outer_flush_range;
break;
default:
BUG();
}

inner_op(start, start + size);
outer_op(__pa(start), __pa(start) + size);
}
EXPORT_SYMBOL(dma_cache_maint);

Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mm/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ void __init bootmem_init(void)
for_each_node(node)
bootmem_free_node(node, mi);

high_memory = __va(memend_pfn << PAGE_SHIFT);
high_memory = __va((memend_pfn << PAGE_SHIFT) - 1) + 1;

/*
* This doesn't seem to be used by the Linux memory manager any
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mm/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ int valid_phys_addr_range(unsigned long addr, size_t size)
{
if (addr < PHYS_OFFSET)
return 0;
if (addr + size > __pa(high_memory))
if (addr + size >= __pa(high_memory - 1))
return 0;

return 1;
Expand Down

0 comments on commit 1522ac3

Please sign in to comment.