blob: 4ca523d58a5b8f3e52cebfa07452a110ee08fbf9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Suspend support specific for i386.
3 *
4 * Distribute under GPLv2
5 *
6 * Copyright (c) 2002 Pavel Machek <pavel@suse.cz>
7 * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
8 */
9
Al Viro55679ed2005-09-12 18:49:24 +020010#include <linux/smp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/suspend.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <asm/proto.h>
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +020013#include <asm/page.h>
14#include <asm/pgtable.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015
Vivek Goyal49c3df62007-05-02 19:27:07 +020016/* References to section boundaries */
17extern const void __nosave_begin, __nosave_end;
18
Linus Torvalds1da177e2005-04-16 15:20:36 -070019struct saved_context saved_context;
20
21unsigned long saved_context_eax, saved_context_ebx, saved_context_ecx, saved_context_edx;
22unsigned long saved_context_esp, saved_context_ebp, saved_context_esi, saved_context_edi;
23unsigned long saved_context_r08, saved_context_r09, saved_context_r10, saved_context_r11;
24unsigned long saved_context_r12, saved_context_r13, saved_context_r14, saved_context_r15;
25unsigned long saved_context_eflags;
26
27void __save_processor_state(struct saved_context *ctxt)
28{
29 kernel_fpu_begin();
30
31 /*
32 * descriptor tables
33 */
34 asm volatile ("sgdt %0" : "=m" (ctxt->gdt_limit));
35 asm volatile ("sidt %0" : "=m" (ctxt->idt_limit));
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 asm volatile ("str %0" : "=m" (ctxt->tr));
37
38 /* XMM0..XMM15 should be handled by kernel_fpu_begin(). */
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 /*
40 * segment registers
41 */
42 asm volatile ("movw %%ds, %0" : "=m" (ctxt->ds));
43 asm volatile ("movw %%es, %0" : "=m" (ctxt->es));
44 asm volatile ("movw %%fs, %0" : "=m" (ctxt->fs));
45 asm volatile ("movw %%gs, %0" : "=m" (ctxt->gs));
46 asm volatile ("movw %%ss, %0" : "=m" (ctxt->ss));
47
48 rdmsrl(MSR_FS_BASE, ctxt->fs_base);
49 rdmsrl(MSR_GS_BASE, ctxt->gs_base);
50 rdmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
51
52 /*
53 * control registers
54 */
Vivek Goyal3c321bc2007-05-02 19:27:07 +020055 rdmsrl(MSR_EFER, ctxt->efer);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 asm volatile ("movq %%cr0, %0" : "=r" (ctxt->cr0));
57 asm volatile ("movq %%cr2, %0" : "=r" (ctxt->cr2));
58 asm volatile ("movq %%cr3, %0" : "=r" (ctxt->cr3));
59 asm volatile ("movq %%cr4, %0" : "=r" (ctxt->cr4));
Pavel Machek8d783b32005-06-25 14:55:14 -070060 asm volatile ("movq %%cr8, %0" : "=r" (ctxt->cr8));
Linus Torvalds1da177e2005-04-16 15:20:36 -070061}
62
63void save_processor_state(void)
64{
65 __save_processor_state(&saved_context);
66}
67
Shaohua Li08967f92005-10-30 14:59:28 -080068static void do_fpu_end(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070069{
Shaohua Li08967f92005-10-30 14:59:28 -080070 /*
71 * Restore FPU regs if necessary
72 */
73 kernel_fpu_end();
Linus Torvalds1da177e2005-04-16 15:20:36 -070074}
75
76void __restore_processor_state(struct saved_context *ctxt)
77{
78 /*
79 * control registers
80 */
Vivek Goyal3c321bc2007-05-02 19:27:07 +020081 wrmsrl(MSR_EFER, ctxt->efer);
Pavel Machek8d783b32005-06-25 14:55:14 -070082 asm volatile ("movq %0, %%cr8" :: "r" (ctxt->cr8));
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 asm volatile ("movq %0, %%cr4" :: "r" (ctxt->cr4));
84 asm volatile ("movq %0, %%cr3" :: "r" (ctxt->cr3));
85 asm volatile ("movq %0, %%cr2" :: "r" (ctxt->cr2));
86 asm volatile ("movq %0, %%cr0" :: "r" (ctxt->cr0));
87
88 /*
Pavel Machek8d783b32005-06-25 14:55:14 -070089 * now restore the descriptor tables to their proper values
90 * ltr is done i fix_processor_context().
91 */
92 asm volatile ("lgdt %0" :: "m" (ctxt->gdt_limit));
93 asm volatile ("lidt %0" :: "m" (ctxt->idt_limit));
94
95 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 * segment registers
97 */
98 asm volatile ("movw %0, %%ds" :: "r" (ctxt->ds));
99 asm volatile ("movw %0, %%es" :: "r" (ctxt->es));
100 asm volatile ("movw %0, %%fs" :: "r" (ctxt->fs));
101 load_gs_index(ctxt->gs);
102 asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss));
103
104 wrmsrl(MSR_FS_BASE, ctxt->fs_base);
105 wrmsrl(MSR_GS_BASE, ctxt->gs_base);
106 wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 fix_processor_context();
109
110 do_fpu_end();
Shaohua Li3b520b22005-07-07 17:56:38 -0700111 mtrr_ap_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112}
113
114void restore_processor_state(void)
115{
116 __restore_processor_state(&saved_context);
117}
118
119void fix_processor_context(void)
120{
121 int cpu = smp_processor_id();
122 struct tss_struct *t = &per_cpu(init_tss, cpu);
123
124 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. */
125
Ravikiran G Thirumalaic11efdf2006-01-11 22:43:57 +0100126 cpu_gdt(cpu)[GDT_ENTRY_TSS].type = 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 syscall_init(); /* This sets MSR_*STAR and related */
129 load_TR_desc(); /* This does ltr */
130 load_LDT(&current->active_mm->context); /* This does lldt */
131
132 /*
133 * Now maybe reload the debug registers
134 */
135 if (current->thread.debugreg7){
136 loaddebug(&current->thread, 0);
137 loaddebug(&current->thread, 1);
138 loaddebug(&current->thread, 2);
139 loaddebug(&current->thread, 3);
140 /* no 4 and 5 */
141 loaddebug(&current->thread, 6);
142 loaddebug(&current->thread, 7);
143 }
144
145}
146
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200147#ifdef CONFIG_SOFTWARE_SUSPEND
148/* Defined in arch/x86_64/kernel/suspend_asm.S */
149extern int restore_image(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200151pgd_t *temp_level4_pgt;
152
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800153static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end)
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200154{
155 long i, j;
156
157 i = pud_index(address);
158 pud = pud + i;
159 for (; i < PTRS_PER_PUD; pud++, i++) {
160 unsigned long paddr;
161 pmd_t *pmd;
162
163 paddr = address + i*PUD_SIZE;
164 if (paddr >= end)
165 break;
166
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800167 pmd = (pmd_t *)get_safe_page(GFP_ATOMIC);
168 if (!pmd)
169 return -ENOMEM;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200170 set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE));
171 for (j = 0; j < PTRS_PER_PMD; pmd++, j++, paddr += PMD_SIZE) {
172 unsigned long pe;
173
174 if (paddr >= end)
175 break;
176 pe = _PAGE_NX | _PAGE_PSE | _KERNPG_TABLE | paddr;
177 pe &= __supported_pte_mask;
178 set_pmd(pmd, __pmd(pe));
179 }
180 }
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800181 return 0;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200182}
183
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800184static int set_up_temporary_mappings(void)
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200185{
186 unsigned long start, end, next;
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800187 int error;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200188
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800189 temp_level4_pgt = (pgd_t *)get_safe_page(GFP_ATOMIC);
190 if (!temp_level4_pgt)
191 return -ENOMEM;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200192
193 /* It is safe to reuse the original kernel mapping */
194 set_pgd(temp_level4_pgt + pgd_index(__START_KERNEL_map),
195 init_level4_pgt[pgd_index(__START_KERNEL_map)]);
196
197 /* Set up the direct mapping from scratch */
198 start = (unsigned long)pfn_to_kaddr(0);
199 end = (unsigned long)pfn_to_kaddr(end_pfn);
200
201 for (; start < end; start = next) {
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800202 pud_t *pud = (pud_t *)get_safe_page(GFP_ATOMIC);
203 if (!pud)
204 return -ENOMEM;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200205 next = start + PGDIR_SIZE;
206 if (next > end)
207 next = end;
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800208 if ((error = res_phys_pud_init(pud, __pa(start), __pa(next))))
209 return error;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200210 set_pgd(temp_level4_pgt + pgd_index(start),
211 mk_kernel_pgd(__pa(pud)));
212 }
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800213 return 0;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200214}
215
216int swsusp_arch_resume(void)
217{
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800218 int error;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200219
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200220 /* We have got enough memory and from now on we cannot recover */
Rafael J. Wysocki2c1b4a52005-10-30 14:59:58 -0800221 if ((error = set_up_temporary_mappings()))
222 return error;
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200223 restore_image();
224 return 0;
225}
Vivek Goyal49c3df62007-05-02 19:27:07 +0200226
227/*
228 * pfn_is_nosave - check if given pfn is in the 'nosave' section
229 */
230
231int pfn_is_nosave(unsigned long pfn)
232{
233 unsigned long nosave_begin_pfn = __pa_symbol(&__nosave_begin) >> PAGE_SHIFT;
234 unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) >> PAGE_SHIFT;
235 return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
236}
Rafael J. Wysocki3dd08322005-10-09 21:19:40 +0200237#endif /* CONFIG_SOFTWARE_SUSPEND */