cpu.c 8.1 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * Suspend support specific for i386/x86-64.
L
Linus Torvalds 已提交
3 4 5
 *
 * Distribute under GPLv2
 *
6
 * Copyright (c) 2007 Rafael J. Wysocki <rjw@sisk.pl>
P
Pavel Machek 已提交
7
 * Copyright (c) 2002 Pavel Machek <pavel@ucw.cz>
L
Linus Torvalds 已提交
8 9 10 11
 * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
 */

#include <linux/suspend.h>
12
#include <linux/export.h>
13 14
#include <linux/smp.h>

15
#include <asm/pgtable.h>
16
#include <asm/proto.h>
17
#include <asm/mtrr.h>
18 19
#include <asm/page.h>
#include <asm/mce.h>
20
#include <asm/xcr.h>
21
#include <asm/suspend.h>
22
#include <asm/debugreg.h>
23
#include <asm/fpu-internal.h> /* pcntxt_mask */
F
Fenghua Yu 已提交
24
#include <asm/cpu.h>
L
Linus Torvalds 已提交
25

26 27
#ifdef CONFIG_X86_32
static struct saved_context saved_context;
28

29 30 31 32 33 34
unsigned long saved_context_ebx;
unsigned long saved_context_esp, saved_context_ebp;
unsigned long saved_context_esi, saved_context_edi;
unsigned long saved_context_eflags;
#else
/* CONFIG_X86_64 */
L
Linus Torvalds 已提交
35
struct saved_context saved_context;
36
#endif
L
Linus Torvalds 已提交
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/**
 *	__save_processor_state - save CPU registers before creating a
 *		hibernation image and before restoring the memory state from it
 *	@ctxt - structure to store the registers contents in
 *
 *	NOTE: If there is a CPU register the modification of which by the
 *	boot kernel (ie. the kernel used for loading the hibernation image)
 *	might affect the operations of the restored target kernel (ie. the one
 *	saved in the hibernation image), then its contents must be saved by this
 *	function.  In other words, if kernel A is hibernated and different
 *	kernel B is used for loading the hibernation image into memory, the
 *	kernel A's __save_processor_state() function must save all registers
 *	needed by kernel A, so that it can operate correctly after the resume
 *	regardless of what kernel B does in the meantime.
 */
53
static void __save_processor_state(struct saved_context *ctxt)
L
Linus Torvalds 已提交
54
{
55 56 57
#ifdef CONFIG_X86_32
	mtrr_save_fixed_ranges(NULL);
#endif
L
Linus Torvalds 已提交
58 59 60 61 62
	kernel_fpu_begin();

	/*
	 * descriptor tables
	 */
63 64 65 66 67
#ifdef CONFIG_X86_32
	store_gdt(&ctxt->gdt);
	store_idt(&ctxt->idt);
#else
/* CONFIG_X86_64 */
68 69
	store_gdt((struct desc_ptr *)&ctxt->gdt_limit);
	store_idt((struct desc_ptr *)&ctxt->idt_limit);
70
#endif
71
	store_tr(ctxt->tr);
L
Linus Torvalds 已提交
72 73 74 75 76

	/* XMM0..XMM15 should be handled by kernel_fpu_begin(). */
	/*
	 * segment registers
	 */
77 78 79 80 81 82 83
#ifdef CONFIG_X86_32
	savesegment(es, ctxt->es);
	savesegment(fs, ctxt->fs);
	savesegment(gs, ctxt->gs);
	savesegment(ss, ctxt->ss);
#else
/* CONFIG_X86_64 */
L
Linus Torvalds 已提交
84 85 86 87 88 89 90 91 92
	asm volatile ("movw %%ds, %0" : "=m" (ctxt->ds));
	asm volatile ("movw %%es, %0" : "=m" (ctxt->es));
	asm volatile ("movw %%fs, %0" : "=m" (ctxt->fs));
	asm volatile ("movw %%gs, %0" : "=m" (ctxt->gs));
	asm volatile ("movw %%ss, %0" : "=m" (ctxt->ss));

	rdmsrl(MSR_FS_BASE, ctxt->fs_base);
	rdmsrl(MSR_GS_BASE, ctxt->gs_base);
	rdmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
93
	mtrr_save_fixed_ranges(NULL);
L
Linus Torvalds 已提交
94

95 96 97
	rdmsrl(MSR_EFER, ctxt->efer);
#endif

L
Linus Torvalds 已提交
98
	/*
99
	 * control registers
L
Linus Torvalds 已提交
100
	 */
101 102 103
	ctxt->cr0 = read_cr0();
	ctxt->cr2 = read_cr2();
	ctxt->cr3 = read_cr3();
104 105 106 107
#ifdef CONFIG_X86_32
	ctxt->cr4 = read_cr4_safe();
#else
/* CONFIG_X86_64 */
108 109
	ctxt->cr4 = read_cr4();
	ctxt->cr8 = read_cr8();
110
#endif
111 112
	ctxt->misc_enable_saved = !rdmsrl_safe(MSR_IA32_MISC_ENABLE,
					       &ctxt->misc_enable);
L
Linus Torvalds 已提交
113 114
}

115
/* Needed by apm.c */
L
Linus Torvalds 已提交
116 117 118
void save_processor_state(void)
{
	__save_processor_state(&saved_context);
119
	x86_platform.save_sched_clock_state();
L
Linus Torvalds 已提交
120
}
121 122 123
#ifdef CONFIG_X86_32
EXPORT_SYMBOL(save_processor_state);
#endif
L
Linus Torvalds 已提交
124

125
static void do_fpu_end(void)
L
Linus Torvalds 已提交
126
{
127
	/*
128
	 * Restore FPU regs if necessary.
129 130
	 */
	kernel_fpu_end();
L
Linus Torvalds 已提交
131 132
}

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
static void fix_processor_context(void)
{
	int cpu = smp_processor_id();
	struct tss_struct *t = &per_cpu(init_tss, cpu);

	set_tss_desc(cpu, t);	/*
				 * This just modifies memory; should not be
				 * necessary. But... This is necessary, because
				 * 386 hardware has concept of busy TSS or some
				 * similar stupidity.
				 */

#ifdef CONFIG_X86_64
	get_cpu_gdt_table(cpu)[GDT_ENTRY_TSS].type = 9;

	syscall_init();				/* This sets MSR_*STAR and related */
#endif
	load_TR_desc();				/* This does ltr */
	load_LDT(&current->active_mm->context);	/* This does lldt */
}

154 155 156 157 158
/**
 *	__restore_processor_state - restore the contents of CPU registers saved
 *		by __save_processor_state()
 *	@ctxt - structure to load the registers contents from
 */
159
static void __restore_processor_state(struct saved_context *ctxt)
L
Linus Torvalds 已提交
160
{
161 162
	if (ctxt->misc_enable_saved)
		wrmsrl(MSR_IA32_MISC_ENABLE, ctxt->misc_enable);
L
Linus Torvalds 已提交
163 164 165
	/*
	 * control registers
	 */
166 167 168 169 170 171
	/* cr4 was introduced in the Pentium CPU */
#ifdef CONFIG_X86_32
	if (ctxt->cr4)
		write_cr4(ctxt->cr4);
#else
/* CONFIG X86_64 */
172
	wrmsrl(MSR_EFER, ctxt->efer);
173 174
	write_cr8(ctxt->cr8);
	write_cr4(ctxt->cr4);
175
#endif
176 177 178
	write_cr3(ctxt->cr3);
	write_cr2(ctxt->cr2);
	write_cr0(ctxt->cr0);
L
Linus Torvalds 已提交
179

180 181 182 183
	/*
	 * now restore the descriptor tables to their proper values
	 * ltr is done i fix_processor_context().
	 */
184 185 186 187 188
#ifdef CONFIG_X86_32
	load_gdt(&ctxt->gdt);
	load_idt(&ctxt->idt);
#else
/* CONFIG_X86_64 */
189 190
	load_gdt((const struct desc_ptr *)&ctxt->gdt_limit);
	load_idt((const struct desc_ptr *)&ctxt->idt_limit);
191
#endif
192

L
Linus Torvalds 已提交
193 194 195
	/*
	 * segment registers
	 */
196 197 198 199 200 201 202 203 204 205 206 207 208
#ifdef CONFIG_X86_32
	loadsegment(es, ctxt->es);
	loadsegment(fs, ctxt->fs);
	loadsegment(gs, ctxt->gs);
	loadsegment(ss, ctxt->ss);

	/*
	 * sysenter MSRs
	 */
	if (boot_cpu_has(X86_FEATURE_SEP))
		enable_sep_cpu();
#else
/* CONFIG_X86_64 */
L
Linus Torvalds 已提交
209 210 211 212 213 214 215 216 217
	asm volatile ("movw %0, %%ds" :: "r" (ctxt->ds));
	asm volatile ("movw %0, %%es" :: "r" (ctxt->es));
	asm volatile ("movw %0, %%fs" :: "r" (ctxt->fs));
	load_gs_index(ctxt->gs);
	asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss));

	wrmsrl(MSR_FS_BASE, ctxt->fs_base);
	wrmsrl(MSR_GS_BASE, ctxt->gs_base);
	wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
218
#endif
L
Linus Torvalds 已提交
219

220 221 222 223 224 225
	/*
	 * restore XCR0 for xsave capable cpu's.
	 */
	if (cpu_has_xsave)
		xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);

L
Linus Torvalds 已提交
226 227 228
	fix_processor_context();

	do_fpu_end();
229
	x86_platform.restore_sched_clock_state();
230
	mtrr_bp_restore();
L
Linus Torvalds 已提交
231 232
}

233
/* Needed by apm.c */
L
Linus Torvalds 已提交
234 235 236 237
void restore_processor_state(void)
{
	__restore_processor_state(&saved_context);
}
238 239 240
#ifdef CONFIG_X86_32
EXPORT_SYMBOL(restore_processor_state);
#endif
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

/*
 * When bsp_check() is called in hibernate and suspend, cpu hotplug
 * is disabled already. So it's unnessary to handle race condition between
 * cpumask query and cpu hotplug.
 */
static int bsp_check(void)
{
	if (cpumask_first(cpu_online_mask) != 0) {
		pr_warn("CPU0 is offline.\n");
		return -ENODEV;
	}

	return 0;
}

static int bsp_pm_callback(struct notifier_block *nb, unsigned long action,
			   void *ptr)
{
	int ret = 0;

	switch (action) {
	case PM_SUSPEND_PREPARE:
	case PM_HIBERNATION_PREPARE:
		ret = bsp_check();
		break;
F
Fenghua Yu 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
#ifdef CONFIG_DEBUG_HOTPLUG_CPU0
	case PM_RESTORE_PREPARE:
		/*
		 * When system resumes from hibernation, online CPU0 because
		 * 1. it's required for resume and
		 * 2. the CPU was online before hibernation
		 */
		if (!cpu_online(0))
			_debug_hotplug_cpu(0, 1);
		break;
	case PM_POST_RESTORE:
		/*
		 * When a resume really happens, this code won't be called.
		 *
		 * This code is called only when user space hibernation software
		 * prepares for snapshot device during boot time. So we just
		 * call _debug_hotplug_cpu() to restore to CPU0's state prior to
		 * preparing the snapshot device.
		 *
		 * This works for normal boot case in our CPU0 hotplug debug
		 * mode, i.e. CPU0 is offline and user mode hibernation
		 * software initializes during boot time.
		 *
		 * If CPU0 is online and user application accesses snapshot
		 * device after boot time, this will offline CPU0 and user may
		 * see different CPU0 state before and after accessing
		 * the snapshot device. But hopefully this is not a case when
		 * user debugging CPU0 hotplug. Even if users hit this case,
		 * they can easily online CPU0 back.
		 *
		 * To simplify this debug code, we only consider normal boot
		 * case. Otherwise we need to remember CPU0's state and restore
		 * to that state and resolve racy conditions etc.
		 */
		_debug_hotplug_cpu(0, 0);
		break;
#endif
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
	default:
		break;
	}
	return notifier_from_errno(ret);
}

static int __init bsp_pm_check_init(void)
{
	/*
	 * Set this bsp_pm_callback as lower priority than
	 * cpu_hotplug_pm_callback. So cpu_hotplug_pm_callback will be called
	 * earlier to disable cpu hotplug before bsp online check.
	 */
	pm_notifier(bsp_pm_callback, -INT_MAX);
	return 0;
}

core_initcall(bsp_pm_check_init);