Merge branches 'x86/paravirt', 'x86/pat', 'x86/setup-v2', 'x86/subarch', 'x86/uaccess...
[deliverable/linux.git] / arch / x86 / mm / pat.c
index 3be399013de620f9fc79ba1ae8b961892c6c5e12..05f9aef6818a14f02c87b7a4e4cf6a0d5d455b71 100644 (file)
@@ -30,7 +30,7 @@
 #ifdef CONFIG_X86_PAT
 int __read_mostly pat_enabled = 1;
 
-void __cpuinit pat_disable(char *reason)
+void __cpuinit pat_disable(const char *reason)
 {
        pat_enabled = 0;
        printk(KERN_INFO "%s\n", reason);
@@ -42,6 +42,11 @@ static int __init nopat(char *str)
        return 0;
 }
 early_param("nopat", nopat);
+#else
+static inline void pat_disable(const char *reason)
+{
+       (void)reason;
+}
 #endif
 
 
@@ -78,16 +83,20 @@ void pat_init(void)
        if (!pat_enabled)
                return;
 
-       /* Paranoia check. */
-       if (!cpu_has_pat && boot_pat_state) {
-               /*
-                * If this happens we are on a secondary CPU, but
-                * switched to PAT on the boot CPU. We have no way to
-                * undo PAT.
-                */
-               printk(KERN_ERR "PAT enabled, "
-                      "but not supported by secondary CPU\n");
-               BUG();
+       if (!cpu_has_pat) {
+               if (!boot_pat_state) {
+                       pat_disable("PAT not supported by CPU.");
+                       return;
+               } else {
+                       /*
+                        * If this happens we are on a secondary CPU, but
+                        * switched to PAT on the boot CPU. We have no way to
+                        * undo PAT.
+                        */
+                       printk(KERN_ERR "PAT enabled, "
+                              "but not supported by secondary CPU\n");
+                       BUG();
+               }
        }
 
        /* Set PWT to Write-Combining. All other bits stay the same */
@@ -211,6 +220,33 @@ chk_conflict(struct memtype *new, struct memtype *entry, unsigned long *type)
 static struct memtype *cached_entry;
 static u64 cached_start;
 
+static int pat_pagerange_is_ram(unsigned long start, unsigned long end)
+{
+       int ram_page = 0, not_rampage = 0;
+       unsigned long page_nr;
+
+       for (page_nr = (start >> PAGE_SHIFT); page_nr < (end >> PAGE_SHIFT);
+            ++page_nr) {
+               /*
+                * For legacy reasons, physical address range in the legacy ISA
+                * region is tracked as non-RAM. This will allow users of
+                * /dev/mem to map portions of legacy ISA region, even when
+                * some of those portions are listed(or not even listed) with
+                * different e820 types(RAM/reserved/..)
+                */
+               if (page_nr >= (ISA_END_ADDRESS >> PAGE_SHIFT) &&
+                   page_is_ram(page_nr))
+                       ram_page = 1;
+               else
+                       not_rampage = 1;
+
+               if (ram_page == not_rampage)
+                       return -1;
+       }
+
+       return ram_page;
+}
+
 /*
  * For RAM pages, mark the pages as non WB memory type using
  * PageNonWB (PG_arch_1). We allow only one set_memory_uc() or
@@ -333,20 +369,15 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
                                              req_type & _PAGE_CACHE_MASK);
        }
 
-       /*
-        * For legacy reasons, some parts of the physical address range in the
-        * legacy 1MB region is treated as non-RAM (even when listed as RAM in
-        * the e820 tables).  So we will track the memory attributes of this
-        * legacy 1MB region using the linear memtype_list always.
-        */
-       if (end >= ISA_END_ADDRESS) {
-               is_range_ram = pagerange_is_ram(start, end);
-               if (is_range_ram == 1)
-                       return reserve_ram_pages_type(start, end, req_type,
-                                                     new_type);
-               else if (is_range_ram < 0)
-                       return -EINVAL;
-       }
+       if (new_type)
+               *new_type = actual_type;
+
+       is_range_ram = pat_pagerange_is_ram(start, end);
+       if (is_range_ram == 1)
+               return reserve_ram_pages_type(start, end, req_type,
+                                             new_type);
+       else if (is_range_ram < 0)
+               return -EINVAL;
 
        new  = kmalloc(sizeof(struct memtype), GFP_KERNEL);
        if (!new)
@@ -356,9 +387,6 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
        new->end        = end;
        new->type       = actual_type;
 
-       if (new_type)
-               *new_type = actual_type;
-
        spin_lock(&memtype_lock);
 
        if (cached_entry && start >= cached_start)
@@ -446,19 +474,11 @@ int free_memtype(u64 start, u64 end)
        if (is_ISA_range(start, end - 1))
                return 0;
 
-       /*
-        * For legacy reasons, some parts of the physical address range in the
-        * legacy 1MB region is treated as non-RAM (even when listed as RAM in
-        * the e820 tables).  So we will track the memory attributes of this
-        * legacy 1MB region using the linear memtype_list always.
-        */
-       if (end >= ISA_END_ADDRESS) {
-               is_range_ram = pagerange_is_ram(start, end);
-               if (is_range_ram == 1)
-                       return free_ram_pages_type(start, end);
-               else if (is_range_ram < 0)
-                       return -EINVAL;
-       }
+       is_range_ram = pat_pagerange_is_ram(start, end);
+       if (is_range_ram == 1)
+               return free_ram_pages_type(start, end);
+       else if (is_range_ram < 0)
+               return -EINVAL;
 
        spin_lock(&memtype_lock);
        list_for_each_entry(entry, &memtype_list, nd) {
@@ -522,35 +542,6 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
 }
 #endif /* CONFIG_STRICT_DEVMEM */
 
-/*
- * Change the memory type for the physial address range in kernel identity
- * mapping space if that range is a part of identity map.
- */
-static int kernel_map_sync_memtype(u64 base, unsigned long size,
-                                       unsigned long flags)
-{
-       unsigned long id_sz;
-       int ret;
-
-       if (!pat_enabled || base >= __pa(high_memory))
-               return 0;
-
-       id_sz = (__pa(high_memory) < base + size) ?
-                                               __pa(high_memory) - base :
-                                               size;
-
-       ret = ioremap_change_attr((unsigned long)__va(base), id_sz, flags);
-       /*
-        * -EFAULT return means that the addr was not valid and did not have
-        * any identity mapping. That case is a success for
-        * kernel_map_sync_memtype.
-        */
-       if (ret == -EFAULT)
-               ret = 0;
-
-       return ret;
-}
-
 int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
                                unsigned long size, pgprot_t *vma_prot)
 {
@@ -601,7 +592,9 @@ int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
        if (retval < 0)
                return 0;
 
-       if (kernel_map_sync_memtype(offset, size, flags)) {
+       if (((pfn < max_low_pfn_mapped) ||
+            (pfn >= (1UL<<(32 - PAGE_SHIFT)) && pfn < max_pfn_mapped)) &&
+           ioremap_change_attr((unsigned long)__va(offset), size, flags) < 0) {
                free_memtype(offset, offset + size);
                printk(KERN_INFO
                "%s:%d /dev/mem ioremap_change_attr failed %s for %Lx-%Lx\n",
@@ -649,21 +642,17 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
                                int strict_prot)
 {
        int is_ram = 0;
-       int ret;
+       int id_sz, ret;
        unsigned long flags;
        unsigned long want_flags = (pgprot_val(*vma_prot) & _PAGE_CACHE_MASK);
 
-       is_ram = pagerange_is_ram(paddr, paddr + size);
+       is_ram = pat_pagerange_is_ram(paddr, paddr + size);
 
-       if (is_ram != 0) {
-               /*
-                * For mapping RAM pages, drivers need to call
-                * set_memory_[uc|wc|wb] directly, for reserve and free, before
-                * setting up the PTE.
-                */
-               WARN_ON_ONCE(1);
-               return 0;
-       }
+       /*
+        * reserve_pfn_range() doesn't support RAM pages.
+        */
+       if (is_ram != 0)
+               return -EINVAL;
 
        ret = reserve_memtype(paddr, paddr + size, want_flags, &flags);
        if (ret)
@@ -690,7 +679,15 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
                                     flags);
        }
 
-       if (kernel_map_sync_memtype(paddr, size, flags)) {
+       /* Need to keep identity mapping in sync */
+       if (paddr >= __pa(high_memory))
+               return 0;
+
+       id_sz = (__pa(high_memory) < paddr + size) ?
+                               __pa(high_memory) - paddr :
+                               size;
+
+       if (ioremap_change_attr((unsigned long)__va(paddr), id_sz, flags) < 0) {
                free_memtype(paddr, paddr + size);
                printk(KERN_ERR
                        "%s:%d reserve_pfn_range ioremap_change_attr failed %s "
@@ -712,7 +709,7 @@ static void free_pfn_range(u64 paddr, unsigned long size)
 {
        int is_ram;
 
-       is_ram = pagerange_is_ram(paddr, paddr + size);
+       is_ram = pat_pagerange_is_ram(paddr, paddr + size);
        if (is_ram == 0)
                free_memtype(paddr, paddr + size);
 }
This page took 0.02996 seconds and 5 git commands to generate.