diff options
Diffstat (limited to 'arch/arm/mm/mmu.c')
| -rw-r--r-- | arch/arm/mm/mmu.c | 74 | 
1 files changed, 74 insertions, 0 deletions
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e5dad60b558..cf4528d5177 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -791,6 +791,79 @@ void __init iotable_init(struct map_desc *io_desc, int nr)  	}  } +#ifndef CONFIG_ARM_LPAE + +/* + * The Linux PMD is made of two consecutive section entries covering 2MB + * (see definition in include/asm/pgtable-2level.h).  However a call to + * create_mapping() may optimize static mappings by using individual + * 1MB section mappings.  This leaves the actual PMD potentially half + * initialized if the top or bottom section entry isn't used, leaving it + * open to problems if a subsequent ioremap() or vmalloc() tries to use + * the virtual space left free by that unused section entry. + * + * Let's avoid the issue by inserting dummy vm entries covering the unused + * PMD halves once the static mappings are in place. + */ + +static void __init pmd_empty_section_gap(unsigned long addr) +{ +	struct vm_struct *vm; + +	vm = early_alloc_aligned(sizeof(*vm), __alignof__(*vm)); +	vm->addr = (void *)addr; +	vm->size = SECTION_SIZE; +	vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING; +	vm->caller = pmd_empty_section_gap; +	vm_area_add_early(vm); +} + +static void __init fill_pmd_gaps(void) +{ +	struct vm_struct *vm; +	unsigned long addr, next = 0; +	pmd_t *pmd; + +	/* we're still single threaded hence no lock needed here */ +	for (vm = vmlist; vm; vm = vm->next) { +		if (!(vm->flags & VM_ARM_STATIC_MAPPING)) +			continue; +		addr = (unsigned long)vm->addr; +		if (addr < next) +			continue; + +		/* +		 * Check if this vm starts on an odd section boundary. +		 * If so and the first section entry for this PMD is free +		 * then we block the corresponding virtual address. +		 */ +		if ((addr & ~PMD_MASK) == SECTION_SIZE) { +			pmd = pmd_off_k(addr); +			if (pmd_none(*pmd)) +				pmd_empty_section_gap(addr & PMD_MASK); +		} + +		/* +		 * Then check if this vm ends on an odd section boundary. +		 * If so and the second section entry for this PMD is empty +		 * then we block the corresponding virtual address. +		 */ +		addr += vm->size; +		if ((addr & ~PMD_MASK) == SECTION_SIZE) { +			pmd = pmd_off_k(addr) + 1; +			if (pmd_none(*pmd)) +				pmd_empty_section_gap(addr); +		} + +		/* no need to look at any vm entry until we hit the next PMD */ +		next = (addr + PMD_SIZE - 1) & PMD_MASK; +	} +} + +#else +#define fill_pmd_gaps() do { } while (0) +#endif +  static void * __initdata vmalloc_min =  	(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET); @@ -1072,6 +1145,7 @@ static void __init devicemaps_init(struct machine_desc *mdesc)  	 */  	if (mdesc->map_io)  		mdesc->map_io(); +	fill_pmd_gaps();  	/*  	 * Finally flush the caches and tlb to ensure that we're in a  |