|
@@ -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 =
|
|
static void * __initdata vmalloc_min =
|
|
(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);
|
|
(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);
|
|
|
|
|
|
@@ -1072,6 +1145,7 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
|
|
*/
|
|
*/
|
|
if (mdesc->map_io)
|
|
if (mdesc->map_io)
|
|
mdesc->map_io();
|
|
mdesc->map_io();
|
|
|
|
+ fill_pmd_gaps();
|
|
|
|
|
|
/*
|
|
/*
|
|
* Finally flush the caches and tlb to ensure that we're in a
|
|
* Finally flush the caches and tlb to ensure that we're in a
|