x86: unify noexec handling
[safe/jmp/linux-2.6] / arch / x86 / mm / ioremap.c
index 8fe576b..8a45093 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/mmiotrace.h>
 
 #include <asm/cacheflush.h>
 #include <asm/e820.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 #include <asm/pgalloc.h>
+#include <asm/pat.h>
 
-enum ioremap_mode {
-       IOR_MODE_UNCACHED,
-       IOR_MODE_CACHED,
-};
+static inline int phys_addr_valid(resource_size_t addr)
+{
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+       return !(addr >> boot_cpu_data.x86_phys_bits);
+#else
+       return 1;
+#endif
+}
 
 #ifdef CONFIG_X86_64
 
 unsigned long __phys_addr(unsigned long x)
 {
-       if (x >= __START_KERNEL_map)
-               return x - __START_KERNEL_map + phys_base;
+       if (x >= __START_KERNEL_map) {
+               x -= __START_KERNEL_map;
+               VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE);
+               x += phys_base;
+       } else {
+               VIRTUAL_BUG_ON(x < PAGE_OFFSET);
+               x -= PAGE_OFFSET;
+               VIRTUAL_BUG_ON(!phys_addr_valid(x));
+       }
+       return x;
+}
+EXPORT_SYMBOL(__phys_addr);
+
+bool __virt_addr_valid(unsigned long x)
+{
+       if (x >= __START_KERNEL_map) {
+               x -= __START_KERNEL_map;
+               if (x >= KERNEL_IMAGE_SIZE)
+                       return false;
+               x += phys_base;
+       } else {
+               if (x < PAGE_OFFSET)
+                       return false;
+               x -= PAGE_OFFSET;
+               if (!phys_addr_valid(x))
+                       return false;
+       }
+
+       return pfn_valid(x >> PAGE_SHIFT);
+}
+EXPORT_SYMBOL(__virt_addr_valid);
+
+#else
+
+#ifdef CONFIG_DEBUG_VIRTUAL
+unsigned long __phys_addr(unsigned long x)
+{
+       /* VMALLOC_* aren't constants  */
+       VIRTUAL_BUG_ON(x < PAGE_OFFSET);
+       VIRTUAL_BUG_ON(__vmalloc_start_set && is_vmalloc_addr((void *) x));
        return x - PAGE_OFFSET;
 }
 EXPORT_SYMBOL(__phys_addr);
+#endif
+
+bool __virt_addr_valid(unsigned long x)
+{
+       if (x < PAGE_OFFSET)
+               return false;
+       if (__vmalloc_start_set && is_vmalloc_addr((void *) x))
+               return false;
+       if (x >= FIXADDR_START)
+               return false;
+       return pfn_valid((x - PAGE_OFFSET) >> PAGE_SHIFT);
+}
+EXPORT_SYMBOL(__virt_addr_valid);
 
 #endif
 
 int page_is_ram(unsigned long pagenr)
 {
-       unsigned long addr, end;
+       resource_size_t addr, end;
        int i;
 
        /*
@@ -78,19 +135,22 @@ int page_is_ram(unsigned long pagenr)
  * Fix up the linear direct mapping of the kernel to avoid cache attribute
  * conflicts.
  */
-static int ioremap_change_attr(unsigned long vaddr, unsigned long size,
-                              enum ioremap_mode mode)
+int ioremap_change_attr(unsigned long vaddr, unsigned long size,
+                              unsigned long prot_val)
 {
        unsigned long nrpages = size >> PAGE_SHIFT;
        int err;
 
-       switch (mode) {
-       case IOR_MODE_UNCACHED:
+       switch (prot_val) {
+       case _PAGE_CACHE_UC:
        default:
-               err = set_memory_uc(vaddr, nrpages);
+               err = _set_memory_uc(vaddr, nrpages);
+               break;
+       case _PAGE_CACHE_WC:
+               err = _set_memory_wc(vaddr, nrpages);
                break;
-       case IOR_MODE_CACHED:
-               err = set_memory_wb(vaddr, nrpages);
+       case _PAGE_CACHE_WB:
+               err = _set_memory_wb(vaddr, nrpages);
                break;
        }
 
@@ -106,42 +166,56 @@ static int ioremap_change_attr(unsigned long vaddr, unsigned long size,
  * have to convert them into an offset in a page-aligned mapping, but the
  * caller shouldn't need to know that small detail.
  */
-static void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
-                              enum ioremap_mode mode)
+static void __iomem *__ioremap_caller(resource_size_t phys_addr,
+               unsigned long size, unsigned long prot_val, void *caller)
 {
-       unsigned long pfn, offset, last_addr, vaddr;
+       unsigned long pfn, offset, vaddr;
+       resource_size_t last_addr;
+       const resource_size_t unaligned_phys_addr = phys_addr;
+       const unsigned long unaligned_size = size;
        struct vm_struct *area;
+       unsigned long new_prot_val;
        pgprot_t prot;
+       int retval;
+       void __iomem *ret_addr;
 
        /* Don't allow wraparound or zero size */
        last_addr = phys_addr + size - 1;
        if (!size || last_addr < phys_addr)
                return NULL;
 
+       if (!phys_addr_valid(phys_addr)) {
+               printk(KERN_WARNING "ioremap: invalid physical address %llx\n",
+                      (unsigned long long)phys_addr);
+               WARN_ON_ONCE(1);
+               return NULL;
+       }
+
        /*
         * Don't remap the low PCI/ISA area, it's always mapped..
         */
-       if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS)
+       if (is_ISA_range(phys_addr, last_addr))
                return (__force void __iomem *)phys_to_virt(phys_addr);
 
        /*
+        * Check if the request spans more than any BAR in the iomem resource
+        * tree.
+        */
+       WARN_ONCE(iomem_map_sanity_check(phys_addr, size),
+                 KERN_INFO "Info: mapping multiple BARs. Your kernel is fine.");
+
+       /*
         * Don't allow anybody to remap normal RAM that we're using..
         */
-       for (pfn = phys_addr >> PAGE_SHIFT; pfn < max_pfn_mapped &&
-            (pfn << PAGE_SHIFT) < last_addr; pfn++) {
-               if (page_is_ram(pfn) && pfn_valid(pfn) &&
-                   !PageReserved(pfn_to_page(pfn)))
-                       return NULL;
-       }
+       for (pfn = phys_addr >> PAGE_SHIFT;
+                               (pfn << PAGE_SHIFT) < (last_addr & PAGE_MASK);
+                               pfn++) {
 
-       switch (mode) {
-       case IOR_MODE_UNCACHED:
-       default:
-               prot = PAGE_KERNEL_NOCACHE;
-               break;
-       case IOR_MODE_CACHED:
-               prot = PAGE_KERNEL;
-               break;
+               int is_ram = page_is_ram(pfn);
+
+               if (is_ram && pfn_valid(pfn) && !PageReserved(pfn_to_page(pfn)))
+                       return NULL;
+               WARN_ON_ONCE(is_ram);
        }
 
        /*
@@ -151,25 +225,78 @@ static void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
        phys_addr &= PAGE_MASK;
        size = PAGE_ALIGN(last_addr+1) - phys_addr;
 
+       retval = reserve_memtype(phys_addr, (u64)phys_addr + size,
+                                               prot_val, &new_prot_val);
+       if (retval) {
+               pr_debug("Warning: reserve_memtype returned %d\n", retval);
+               return NULL;
+       }
+
+       if (prot_val != new_prot_val) {
+               /*
+                * Do not fallback to certain memory types with certain
+                * requested type:
+                * - request is uc-, return cannot be write-back
+                * - request is uc-, return cannot be write-combine
+                * - request is write-combine, return cannot be write-back
+                */
+               if ((prot_val == _PAGE_CACHE_UC_MINUS &&
+                    (new_prot_val == _PAGE_CACHE_WB ||
+                     new_prot_val == _PAGE_CACHE_WC)) ||
+                   (prot_val == _PAGE_CACHE_WC &&
+                    new_prot_val == _PAGE_CACHE_WB)) {
+                       pr_debug(
+               "ioremap error for 0x%llx-0x%llx, requested 0x%lx, got 0x%lx\n",
+                               (unsigned long long)phys_addr,
+                               (unsigned long long)(phys_addr + size),
+                               prot_val, new_prot_val);
+                       free_memtype(phys_addr, phys_addr + size);
+                       return NULL;
+               }
+               prot_val = new_prot_val;
+       }
+
+       switch (prot_val) {
+       case _PAGE_CACHE_UC:
+       default:
+               prot = PAGE_KERNEL_IO_NOCACHE;
+               break;
+       case _PAGE_CACHE_UC_MINUS:
+               prot = PAGE_KERNEL_IO_UC_MINUS;
+               break;
+       case _PAGE_CACHE_WC:
+               prot = PAGE_KERNEL_IO_WC;
+               break;
+       case _PAGE_CACHE_WB:
+               prot = PAGE_KERNEL_IO;
+               break;
+       }
+
        /*
         * Ok, go for it..
         */
-       area = get_vm_area(size, VM_IOREMAP);
+       area = get_vm_area_caller(size, VM_IOREMAP, caller);
        if (!area)
                return NULL;
        area->phys_addr = phys_addr;
        vaddr = (unsigned long) area->addr;
-       if (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot)) {
+
+       if (kernel_map_sync_memtype(phys_addr, size, prot_val)) {
+               free_memtype(phys_addr, phys_addr + size);
                free_vm_area(area);
                return NULL;
        }
 
-       if (ioremap_change_attr(vaddr, size, mode) < 0) {
-               vunmap(area->addr);
+       if (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot)) {
+               free_memtype(phys_addr, phys_addr + size);
+               free_vm_area(area);
                return NULL;
        }
 
-       return (void __iomem *) (vaddr + offset);
+       ret_addr = (void __iomem *) (vaddr + offset);
+       mmiotrace_ioremap(unaligned_phys_addr, unaligned_size, ret_addr);
+
+       return ret_addr;
 }
 
 /**
@@ -193,18 +320,81 @@ static void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
  *
  * Must be freed with iounmap.
  */
-void __iomem *ioremap_nocache(unsigned long phys_addr, unsigned long size)
+void __iomem *ioremap_nocache(resource_size_t phys_addr, unsigned long size)
 {
-       return __ioremap(phys_addr, size, IOR_MODE_UNCACHED);
+       /*
+        * Ideally, this should be:
+        *      pat_enabled ? _PAGE_CACHE_UC : _PAGE_CACHE_UC_MINUS;
+        *
+        * Till we fix all X drivers to use ioremap_wc(), we will use
+        * UC MINUS.
+        */
+       unsigned long val = _PAGE_CACHE_UC_MINUS;
+
+       return __ioremap_caller(phys_addr, size, val,
+                               __builtin_return_address(0));
 }
 EXPORT_SYMBOL(ioremap_nocache);
 
-void __iomem *ioremap_cache(unsigned long phys_addr, unsigned long size)
+/**
+ * ioremap_wc  -       map memory into CPU space write combined
+ * @offset:    bus address of the memory
+ * @size:      size of the resource to map
+ *
+ * This version of ioremap ensures that the memory is marked write combining.
+ * Write combining allows faster writes to some hardware devices.
+ *
+ * Must be freed with iounmap.
+ */
+void __iomem *ioremap_wc(resource_size_t phys_addr, unsigned long size)
+{
+       if (pat_enabled)
+               return __ioremap_caller(phys_addr, size, _PAGE_CACHE_WC,
+                                       __builtin_return_address(0));
+       else
+               return ioremap_nocache(phys_addr, size);
+}
+EXPORT_SYMBOL(ioremap_wc);
+
+void __iomem *ioremap_cache(resource_size_t phys_addr, unsigned long size)
 {
-       return __ioremap(phys_addr, size, IOR_MODE_CACHED);
+       return __ioremap_caller(phys_addr, size, _PAGE_CACHE_WB,
+                               __builtin_return_address(0));
 }
 EXPORT_SYMBOL(ioremap_cache);
 
+static void __iomem *ioremap_default(resource_size_t phys_addr,
+                                       unsigned long size)
+{
+       unsigned long flags;
+       void __iomem *ret;
+       int err;
+
+       /*
+        * - WB for WB-able memory and no other conflicting mappings
+        * - UC_MINUS for non-WB-able memory with no other conflicting mappings
+        * - Inherit from confliting mappings otherwise
+        */
+       err = reserve_memtype(phys_addr, phys_addr + size,
+                               _PAGE_CACHE_WB, &flags);
+       if (err < 0)
+               return NULL;
+
+       ret = __ioremap_caller(phys_addr, size, flags,
+                              __builtin_return_address(0));
+
+       free_memtype(phys_addr, phys_addr + size);
+       return ret;
+}
+
+void __iomem *ioremap_prot(resource_size_t phys_addr, unsigned long size,
+                               unsigned long prot_val)
+{
+       return __ioremap_caller(phys_addr, size, (prot_val & _PAGE_CACHE_MASK),
+                               __builtin_return_address(0));
+}
+EXPORT_SYMBOL(ioremap_prot);
+
 /**
  * iounmap - Free a IO remapping
  * @addr: virtual address from ioremap_*
@@ -223,13 +413,15 @@ void iounmap(volatile void __iomem *addr)
         * vm_area and by simply returning an address into the kernel mapping
         * of ISA space.   So handle that here.
         */
-       if (addr >= phys_to_virt(ISA_START_ADDRESS) &&
-           addr < phys_to_virt(ISA_END_ADDRESS))
+       if ((void __force *)addr >= phys_to_virt(ISA_START_ADDRESS) &&
+           (void __force *)addr < phys_to_virt(ISA_END_ADDRESS))
                return;
 
        addr = (volatile void __iomem *)
                (PAGE_MASK & (unsigned long __force)addr);
 
+       mmiotrace_iounmap(addr);
+
        /* Use the vm area unlocked, assuming the caller
           ensures there isn't another iounmap for the same address
           in parallel. Reuse of the virtual address is prevented by
@@ -237,7 +429,7 @@ void iounmap(volatile void __iomem *addr)
           cpa takes care of the direct mappings. */
        read_lock(&vmlist_lock);
        for (p = vmlist; p; p = p->next) {
-               if (p->addr == addr)
+               if (p->addr == (void __force *)addr)
                        break;
        }
        read_unlock(&vmlist_lock);
@@ -248,16 +440,45 @@ void iounmap(volatile void __iomem *addr)
                return;
        }
 
+       free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p));
+
        /* Finally remove it */
-       o = remove_vm_area((void *)addr);
+       o = remove_vm_area((void __force *)addr);
        BUG_ON(p != o || o == NULL);
        kfree(p);
 }
 EXPORT_SYMBOL(iounmap);
 
-#ifdef CONFIG_X86_32
+/*
+ * Convert a physical pointer to a virtual kernel pointer for /dev/mem
+ * access
+ */
+void *xlate_dev_mem_ptr(unsigned long phys)
+{
+       void *addr;
+       unsigned long start = phys & PAGE_MASK;
+
+       /* If page is RAM, we can use __va. Otherwise ioremap and unmap. */
+       if (page_is_ram(start >> PAGE_SHIFT))
+               return __va(phys);
 
-int __initdata early_ioremap_debug;
+       addr = (void __force *)ioremap_default(start, PAGE_SIZE);
+       if (addr)
+               addr = (void *)((unsigned long)addr | (phys & ~PAGE_MASK));
+
+       return addr;
+}
+
+void unxlate_dev_mem_ptr(unsigned long phys, void *addr)
+{
+       if (page_is_ram(phys >> PAGE_SHIFT))
+               return;
+
+       iounmap((void __iomem *)((unsigned long)addr & PAGE_MASK));
+       return;
+}
+
+static int __initdata early_ioremap_debug;
 
 static int __init early_ioremap_debug_setup(char *str)
 {
@@ -268,8 +489,7 @@ static int __init early_ioremap_debug_setup(char *str)
 early_param("early_ioremap_debug", early_ioremap_debug_setup);
 
 static __initdata int after_paging_init;
-static __initdata pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)]
-                               __attribute__((aligned(PAGE_SIZE)));
+static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] __page_aligned_bss;
 
 static inline pmd_t * __init early_ioremap_pmd(unsigned long addr)
 {
@@ -287,13 +507,19 @@ static inline pte_t * __init early_ioremap_pte(unsigned long addr)
        return &bm_pte[pte_index(addr)];
 }
 
+static unsigned long slot_virt[FIX_BTMAPS_SLOTS] __initdata;
+
 void __init early_ioremap_init(void)
 {
        pmd_t *pmd;
+       int i;
 
        if (early_ioremap_debug)
                printk(KERN_INFO "early_ioremap_init()\n");
 
+       for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
+               slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i);
+
        pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN));
        memset(bm_pte, 0, sizeof(bm_pte));
        pmd_populate_kernel(&init_mm, pmd, bm_pte);
@@ -317,38 +543,13 @@ void __init early_ioremap_init(void)
        }
 }
 
-void __init early_ioremap_clear(void)
-{
-       pmd_t *pmd;
-
-       if (early_ioremap_debug)
-               printk(KERN_INFO "early_ioremap_clear()\n");
-
-       pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN));
-       pmd_clear(pmd);
-       paravirt_release_pt(__pa(bm_pte) >> PAGE_SHIFT);
-       __flush_tlb_all();
-}
-
 void __init early_ioremap_reset(void)
 {
-       enum fixed_addresses idx;
-       unsigned long addr, phys;
-       pte_t *pte;
-
        after_paging_init = 1;
-       for (idx = FIX_BTMAP_BEGIN; idx >= FIX_BTMAP_END; idx--) {
-               addr = fix_to_virt(idx);
-               pte = early_ioremap_pte(addr);
-               if (pte_present(*pte)) {
-                       phys = pte_val(*pte) & PAGE_MASK;
-                       set_fixmap(idx, phys);
-               }
-       }
 }
 
 static void __init __early_set_fixmap(enum fixed_addresses idx,
-                                  unsigned long phys, pgprot_t flags)
+                                     phys_addr_t phys, pgprot_t flags)
 {
        unsigned long addr = __fix_to_virt(idx);
        pte_t *pte;
@@ -358,20 +559,21 @@ static void __init __early_set_fixmap(enum fixed_addresses idx,
                return;
        }
        pte = early_ioremap_pte(addr);
+
        if (pgprot_val(flags))
                set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
        else
-               pte_clear(NULL, addr, pte);
+               pte_clear(&init_mm, addr, pte);
        __flush_tlb_one(addr);
 }
 
 static inline void __init early_set_fixmap(enum fixed_addresses idx,
-                                       unsigned long phys)
+                                          phys_addr_t phys, pgprot_t prot)
 {
        if (after_paging_init)
-               set_fixmap(idx, phys);
+               __set_fixmap(idx, phys, prot);
        else
-               __early_set_fixmap(idx, phys, PAGE_KERNEL);
+               __early_set_fixmap(idx, phys, prot);
 }
 
 static inline void __init early_clear_fixmap(enum fixed_addresses idx)
@@ -382,37 +584,59 @@ static inline void __init early_clear_fixmap(enum fixed_addresses idx)
                __early_set_fixmap(idx, 0, __pgprot(0));
 }
 
-
-int __initdata early_ioremap_nested;
+static void __iomem *prev_map[FIX_BTMAPS_SLOTS] __initdata;
+static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata;
 
 static int __init check_early_ioremap_leak(void)
 {
-       if (!early_ioremap_nested)
-               return 0;
+       int count = 0;
+       int i;
 
-       printk(KERN_WARNING
+       for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
+               if (prev_map[i])
+                       count++;
+
+       if (!count)
+               return 0;
+       WARN(1, KERN_WARNING
               "Debug warning: early ioremap leak of %d areas detected.\n",
-              early_ioremap_nested);
+               count);
        printk(KERN_WARNING
-              "please boot with early_ioremap_debug and report the dmesg.\n");
-       WARN_ON(1);
+               "please boot with early_ioremap_debug and report the dmesg.\n");
 
        return 1;
 }
 late_initcall(check_early_ioremap_leak);
 
-void __init *early_ioremap(unsigned long phys_addr, unsigned long size)
+static void __init __iomem *
+__early_ioremap(resource_size_t phys_addr, unsigned long size, pgprot_t prot)
 {
-       unsigned long offset, last_addr;
-       unsigned int nrpages, nesting;
+       unsigned long offset;
+       resource_size_t last_addr;
+       unsigned int nrpages;
        enum fixed_addresses idx0, idx;
+       int i, slot;
 
        WARN_ON(system_state != SYSTEM_BOOTING);
 
-       nesting = early_ioremap_nested;
+       slot = -1;
+       for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
+               if (!prev_map[i]) {
+                       slot = i;
+                       break;
+               }
+       }
+
+       if (slot < 0) {
+               printk(KERN_INFO "early_iomap(%08llx, %08lx) not found slot\n",
+                        (u64)phys_addr, size);
+               WARN_ON(1);
+               return NULL;
+       }
+
        if (early_ioremap_debug) {
-               printk(KERN_INFO "early_ioremap(%08lx, %08lx) [%d] => ",
-                      phys_addr, size, nesting);
+               printk(KERN_INFO "early_ioremap(%08llx, %08lx) [%d] => ",
+                      (u64)phys_addr, size, slot);
                dump_stack();
        }
 
@@ -423,17 +647,13 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size)
                return NULL;
        }
 
-       if (nesting >= FIX_BTMAPS_NESTING) {
-               WARN_ON(1);
-               return NULL;
-       }
-       early_ioremap_nested++;
+       prev_size[slot] = size;
        /*
         * Mappings have to be page-aligned
         */
        offset = phys_addr & ~PAGE_MASK;
        phys_addr &= PAGE_MASK;
-       size = PAGE_ALIGN(last_addr) - phys_addr;
+       size = PAGE_ALIGN(last_addr + 1) - phys_addr;
 
        /*
         * Mappings have to fit in the FIX_BTMAP area.
@@ -447,34 +667,68 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size)
        /*
         * Ok, go for it..
         */
-       idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting;
+       idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
        idx = idx0;
        while (nrpages > 0) {
-               early_set_fixmap(idx, phys_addr);
+               early_set_fixmap(idx, phys_addr, prot);
                phys_addr += PAGE_SIZE;
                --idx;
                --nrpages;
        }
        if (early_ioremap_debug)
-               printk(KERN_CONT "%08lx + %08lx\n", offset, fix_to_virt(idx0));
+               printk(KERN_CONT "%08lx + %08lx\n", offset, slot_virt[slot]);
+
+       prev_map[slot] = (void __iomem *)(offset + slot_virt[slot]);
+       return prev_map[slot];
+}
 
-       return (void *) (offset + fix_to_virt(idx0));
+/* Remap an IO device */
+void __init __iomem *
+early_ioremap(resource_size_t phys_addr, unsigned long size)
+{
+       return __early_ioremap(phys_addr, size, PAGE_KERNEL_IO);
 }
 
-void __init early_iounmap(void *addr, unsigned long size)
+/* Remap memory */
+void __init __iomem *
+early_memremap(resource_size_t phys_addr, unsigned long size)
+{
+       return __early_ioremap(phys_addr, size, PAGE_KERNEL);
+}
+
+void __init early_iounmap(void __iomem *addr, unsigned long size)
 {
        unsigned long virt_addr;
        unsigned long offset;
        unsigned int nrpages;
        enum fixed_addresses idx;
-       unsigned int nesting;
+       int i, slot;
+
+       slot = -1;
+       for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
+               if (prev_map[i] == addr) {
+                       slot = i;
+                       break;
+               }
+       }
+
+       if (slot < 0) {
+               printk(KERN_INFO "early_iounmap(%p, %08lx) not found slot\n",
+                        addr, size);
+               WARN_ON(1);
+               return;
+       }
 
-       nesting = --early_ioremap_nested;
-       WARN_ON(nesting < 0);
+       if (prev_size[slot] != size) {
+               printk(KERN_INFO "early_iounmap(%p, %08lx) [%d] size not consistent %08lx\n",
+                        addr, size, slot, prev_size[slot]);
+               WARN_ON(1);
+               return;
+       }
 
        if (early_ioremap_debug) {
                printk(KERN_INFO "early_iounmap(%p, %08lx) [%d]\n", addr,
-                      size, nesting);
+                      size, slot);
                dump_stack();
        }
 
@@ -486,17 +740,11 @@ void __init early_iounmap(void *addr, unsigned long size)
        offset = virt_addr & ~PAGE_MASK;
        nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT;
 
-       idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting;
+       idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
        while (nrpages > 0) {
                early_clear_fixmap(idx);
                --idx;
                --nrpages;
        }
+       prev_map[slot] = NULL;
 }
-
-void __this_fixmap_does_not_exist(void)
-{
-       WARN_ON(1);
-}
-
-#endif /* CONFIG_X86_32 */