Merge branch 'drm-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
[linux-drm-fsl-dcu.git] / arch / x86_64 / kernel / suspend.c
index f066c6ab3618f24a5d7d7323de14d4a9cd0c1144..6a5a98f2a75c5aac97d543f116db736182b6e73c 100644 (file)
@@ -7,12 +7,15 @@
  * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
  */
 
-#include <linux/config.h>
 #include <linux/smp.h>
 #include <linux/suspend.h>
 #include <asm/proto.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
+#include <asm/mtrr.h>
+
+/* References to section boundaries */
+extern const void __nosave_begin, __nosave_end;
 
 struct saved_context saved_context;
 
@@ -34,7 +37,6 @@ void __save_processor_state(struct saved_context *ctxt)
        asm volatile ("str %0"  : "=m" (ctxt->tr));
 
        /* XMM0..XMM15 should be handled by kernel_fpu_begin(). */
-       /* EFER should be constant for kernel version, no need to handle it. */
        /*
         * segment registers
         */
@@ -47,10 +49,12 @@ void __save_processor_state(struct saved_context *ctxt)
        rdmsrl(MSR_FS_BASE, ctxt->fs_base);
        rdmsrl(MSR_GS_BASE, ctxt->gs_base);
        rdmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
+       mtrr_save_fixed_ranges(NULL);
 
        /*
         * control registers 
         */
+       rdmsrl(MSR_EFER, ctxt->efer);
        asm volatile ("movq %%cr0, %0" : "=r" (ctxt->cr0));
        asm volatile ("movq %%cr2, %0" : "=r" (ctxt->cr2));
        asm volatile ("movq %%cr3, %0" : "=r" (ctxt->cr3));
@@ -63,13 +67,12 @@ void save_processor_state(void)
        __save_processor_state(&saved_context);
 }
 
-static void
-do_fpu_end(void)
+static void do_fpu_end(void)
 {
-        /* restore FPU regs if necessary */
-       /* Do it out of line so that gcc does not move cr0 load to some stupid place */
-        kernel_fpu_end();
-       mxcsr_feature_mask_init();
+       /*
+        * Restore FPU regs if necessary
+        */
+       kernel_fpu_end();
 }
 
 void __restore_processor_state(struct saved_context *ctxt)
@@ -77,6 +80,7 @@ void __restore_processor_state(struct saved_context *ctxt)
        /*
         * control registers
         */
+       wrmsrl(MSR_EFER, ctxt->efer);
        asm volatile ("movq %0, %%cr8" :: "r" (ctxt->cr8));
        asm volatile ("movq %0, %%cr4" :: "r" (ctxt->cr4));
        asm volatile ("movq %0, %%cr3" :: "r" (ctxt->cr3));
@@ -121,7 +125,7 @@ void fix_processor_context(void)
 
        set_tss_desc(cpu,t);    /* This just modifies memory; should not be neccessary. But... This is neccessary, because 386 hardware has concept of busy TSS or some similar stupidity. */
 
-       cpu_gdt_table[cpu][GDT_ENTRY_TSS].type = 9;
+       cpu_gdt(cpu)[GDT_ENTRY_TSS].type = 9;
 
        syscall_init();                         /* This sets MSR_*STAR and related */
        load_TR_desc();                         /* This does ltr */
@@ -148,57 +152,7 @@ extern int restore_image(void);
 
 pgd_t *temp_level4_pgt;
 
-static void **pages;
-
-static inline void *__add_page(void)
-{
-       void **c;
-
-       c = (void **)get_usable_page(GFP_ATOMIC);
-       if (c) {
-               *c = pages;
-               pages = c;
-       }
-       return c;
-}
-
-static inline void *__next_page(void)
-{
-       void **c;
-
-       c = pages;
-       if (c) {
-               pages = *c;
-               *c = NULL;
-       }
-       return c;
-}
-
-/*
- * Try to allocate as many usable pages as needed and daisy chain them.
- * If one allocation fails, free the pages allocated so far
- */
-static int alloc_usable_pages(unsigned long n)
-{
-       void *p;
-
-       pages = NULL;
-       do
-               if (!__add_page())
-                       break;
-       while (--n);
-       if (n) {
-               p = __next_page();
-               while (p) {
-                       free_page((unsigned long)p);
-                       p = __next_page();
-               }
-               return -ENOMEM;
-       }
-       return 0;
-}
-
-static void res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end)
+static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end)
 {
        long i, j;
 
@@ -212,7 +166,9 @@ static void res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long e
                if (paddr >= end)
                        break;
 
-               pmd = (pmd_t *)__next_page();
+               pmd = (pmd_t *)get_safe_page(GFP_ATOMIC);
+               if (!pmd)
+                       return -ENOMEM;
                set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE));
                for (j = 0; j < PTRS_PER_PMD; pmd++, j++, paddr += PMD_SIZE) {
                        unsigned long pe;
@@ -224,13 +180,17 @@ static void res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long e
                        set_pmd(pmd, __pmd(pe));
                }
        }
+       return 0;
 }
 
-static void set_up_temporary_mappings(void)
+static int set_up_temporary_mappings(void)
 {
        unsigned long start, end, next;
+       int error;
 
-       temp_level4_pgt = (pgd_t *)__next_page();
+       temp_level4_pgt = (pgd_t *)get_safe_page(GFP_ATOMIC);
+       if (!temp_level4_pgt)
+               return -ENOMEM;
 
        /* It is safe to reuse the original kernel mapping */
        set_pgd(temp_level4_pgt + pgd_index(__START_KERNEL_map),
@@ -241,30 +201,39 @@ static void set_up_temporary_mappings(void)
        end = (unsigned long)pfn_to_kaddr(end_pfn);
 
        for (; start < end; start = next) {
-               pud_t *pud = (pud_t *)__next_page();
+               pud_t *pud = (pud_t *)get_safe_page(GFP_ATOMIC);
+               if (!pud)
+                       return -ENOMEM;
                next = start + PGDIR_SIZE;
                if (next > end)
                        next = end;
-               res_phys_pud_init(pud, __pa(start), __pa(next));
+               if ((error = res_phys_pud_init(pud, __pa(start), __pa(next))))
+                       return error;
                set_pgd(temp_level4_pgt + pgd_index(start),
                        mk_kernel_pgd(__pa(pud)));
        }
+       return 0;
 }
 
 int swsusp_arch_resume(void)
 {
-       unsigned long n;
+       int error;
 
-       n = ((end_pfn << PAGE_SHIFT) + PUD_SIZE - 1) >> PUD_SHIFT;
-       n += (n + PTRS_PER_PUD - 1) / PTRS_PER_PUD + 1;
-       pr_debug("swsusp_arch_resume(): pages needed = %lu\n", n);
-       if (alloc_usable_pages(n)) {
-               free_eaten_memory();
-               return -ENOMEM;
-       }
        /* We have got enough memory and from now on we cannot recover */
-       set_up_temporary_mappings();
+       if ((error = set_up_temporary_mappings()))
+               return error;
        restore_image();
        return 0;
 }
+
+/*
+ *     pfn_is_nosave - check if given pfn is in the 'nosave' section
+ */
+
+int pfn_is_nosave(unsigned long pfn)
+{
+       unsigned long nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT;
+       unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT;
+       return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
+}
 #endif /* CONFIG_SOFTWARE_SUSPEND */