/root/src/xen/xen/arch/x86/acpi/power.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* drivers/acpi/sleep/power.c - PM core functionality for Xen |
2 | | * |
3 | | * Copyrights from Linux side: |
4 | | * Copyright (c) 2000-2003 Patrick Mochel |
5 | | * Copyright (C) 2001-2003 Pavel Machek <pavel@suse.cz> |
6 | | * Copyright (c) 2003 Open Source Development Lab |
7 | | * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com> |
8 | | * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> |
9 | | * |
10 | | * Slimmed with Xen specific support. |
11 | | */ |
12 | | |
13 | | #include <asm/io.h> |
14 | | #include <xen/acpi.h> |
15 | | #include <xen/errno.h> |
16 | | #include <xen/iocap.h> |
17 | | #include <xen/sched.h> |
18 | | #include <asm/acpi.h> |
19 | | #include <asm/irq.h> |
20 | | #include <asm/init.h> |
21 | | #include <xen/spinlock.h> |
22 | | #include <xen/sched.h> |
23 | | #include <xen/domain.h> |
24 | | #include <xen/console.h> |
25 | | #include <xen/iommu.h> |
26 | | #include <xen/cpu.h> |
27 | | #include <public/platform.h> |
28 | | #include <asm/tboot.h> |
29 | | #include <asm/apic.h> |
30 | | #include <asm/io_apic.h> |
31 | | #include <acpi/cpufreq/cpufreq.h> |
32 | | |
33 | | uint32_t system_reset_counter = 1; |
34 | | |
35 | | static char __initdata opt_acpi_sleep[20]; |
36 | | string_param("acpi_sleep", opt_acpi_sleep); |
37 | | |
38 | | static u8 sleep_states[ACPI_S_STATE_COUNT]; |
39 | | static DEFINE_SPINLOCK(pm_lock); |
40 | | |
41 | | struct acpi_sleep_info acpi_sinfo; |
42 | | |
43 | | void do_suspend_lowlevel(void); |
44 | | |
45 | | enum dev_power_saved |
46 | | { |
47 | | SAVED_NONE, |
48 | | SAVED_CONSOLE, |
49 | | SAVED_TIME, |
50 | | SAVED_I8259A, |
51 | | SAVED_IOAPIC, |
52 | | SAVED_IOMMU, |
53 | | SAVED_LAPIC, |
54 | | SAVED_ALL, |
55 | | }; |
56 | | |
57 | | static int device_power_down(void) |
58 | 0 | { |
59 | 0 | if ( console_suspend() ) |
60 | 0 | return SAVED_NONE; |
61 | 0 |
|
62 | 0 | if ( time_suspend() ) |
63 | 0 | return SAVED_CONSOLE; |
64 | 0 |
|
65 | 0 | if ( i8259A_suspend() ) |
66 | 0 | return SAVED_TIME; |
67 | 0 |
|
68 | 0 | /* ioapic_suspend cannot fail */ |
69 | 0 | ioapic_suspend(); |
70 | 0 |
|
71 | 0 | if ( iommu_suspend() ) |
72 | 0 | return SAVED_IOAPIC; |
73 | 0 |
|
74 | 0 | if ( lapic_suspend() ) |
75 | 0 | return SAVED_IOMMU; |
76 | 0 |
|
77 | 0 | return SAVED_ALL; |
78 | 0 | } |
79 | | |
80 | | static void device_power_up(enum dev_power_saved saved) |
81 | 0 | { |
82 | 0 | switch ( saved ) |
83 | 0 | { |
84 | 0 | case SAVED_ALL: |
85 | 0 | case SAVED_LAPIC: |
86 | 0 | lapic_resume(); |
87 | 0 | /* fall through */ |
88 | 0 | case SAVED_IOMMU: |
89 | 0 | iommu_resume(); |
90 | 0 | /* fall through */ |
91 | 0 | case SAVED_IOAPIC: |
92 | 0 | ioapic_resume(); |
93 | 0 | /* fall through */ |
94 | 0 | case SAVED_I8259A: |
95 | 0 | i8259A_resume(); |
96 | 0 | /* fall through */ |
97 | 0 | case SAVED_TIME: |
98 | 0 | time_resume(); |
99 | 0 | /* fall through */ |
100 | 0 | case SAVED_CONSOLE: |
101 | 0 | console_resume(); |
102 | 0 | /* fall through */ |
103 | 0 | case SAVED_NONE: |
104 | 0 | break; |
105 | 0 | default: |
106 | 0 | BUG(); |
107 | 0 | break; |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | static void freeze_domains(void) |
112 | 0 | { |
113 | 0 | struct domain *d; |
114 | 0 |
|
115 | 0 | rcu_read_lock(&domlist_read_lock); |
116 | 0 | /* |
117 | 0 | * Note that we iterate in order of domain-id. Hence we will pause dom0 |
118 | 0 | * first which is required for correctness (as only dom0 can add domains to |
119 | 0 | * the domain list). Otherwise we could miss concurrently-created domains. |
120 | 0 | */ |
121 | 0 | for_each_domain ( d ) |
122 | 0 | domain_pause(d); |
123 | 0 | rcu_read_unlock(&domlist_read_lock); |
124 | 0 | } |
125 | | |
126 | | static void thaw_domains(void) |
127 | 0 | { |
128 | 0 | struct domain *d; |
129 | 0 |
|
130 | 0 | rcu_read_lock(&domlist_read_lock); |
131 | 0 | for_each_domain ( d ) |
132 | 0 | { |
133 | 0 | restore_vcpu_affinity(d); |
134 | 0 | domain_unpause(d); |
135 | 0 | } |
136 | 0 | rcu_read_unlock(&domlist_read_lock); |
137 | 0 | } |
138 | | |
139 | | static void acpi_sleep_prepare(u32 state) |
140 | 0 | { |
141 | 0 | void *wakeup_vector_va; |
142 | 0 |
|
143 | 0 | if ( state != ACPI_STATE_S3 ) |
144 | 0 | return; |
145 | 0 |
|
146 | 0 | wakeup_vector_va = __acpi_map_table( |
147 | 0 | acpi_sinfo.wakeup_vector, sizeof(uint64_t)); |
148 | 0 |
|
149 | 0 | /* TBoot will set resume vector itself (when it is safe to do so). */ |
150 | 0 | if ( tboot_in_measured_env() ) |
151 | 0 | return; |
152 | 0 |
|
153 | 0 | if ( acpi_sinfo.vector_width == 32 ) |
154 | 0 | *(uint32_t *)wakeup_vector_va = bootsym_phys(wakeup_start); |
155 | 0 | else |
156 | 0 | *(uint64_t *)wakeup_vector_va = bootsym_phys(wakeup_start); |
157 | 0 | } |
158 | | |
159 | 0 | static void acpi_sleep_post(u32 state) {} |
160 | | |
161 | | /* Main interface to do xen specific suspend/resume */ |
162 | | static int enter_state(u32 state) |
163 | 0 | { |
164 | 0 | unsigned long flags; |
165 | 0 | int error; |
166 | 0 | unsigned long cr4; |
167 | 0 |
|
168 | 0 | if ( (state <= ACPI_STATE_S0) || (state > ACPI_S_STATES_MAX) ) |
169 | 0 | return -EINVAL; |
170 | 0 |
|
171 | 0 | if ( !spin_trylock(&pm_lock) ) |
172 | 0 | return -EBUSY; |
173 | 0 |
|
174 | 0 | BUG_ON(system_state != SYS_STATE_active); |
175 | 0 | system_state = SYS_STATE_suspend; |
176 | 0 |
|
177 | 0 | printk(XENLOG_INFO "Preparing system for ACPI S%d state.\n", state); |
178 | 0 |
|
179 | 0 | freeze_domains(); |
180 | 0 |
|
181 | 0 | acpi_dmar_reinstate(); |
182 | 0 |
|
183 | 0 | if ( (error = disable_nonboot_cpus()) ) |
184 | 0 | { |
185 | 0 | system_state = SYS_STATE_resume; |
186 | 0 | goto enable_cpu; |
187 | 0 | } |
188 | 0 |
|
189 | 0 | cpufreq_del_cpu(0); |
190 | 0 |
|
191 | 0 | hvm_cpu_down(); |
192 | 0 |
|
193 | 0 | acpi_sleep_prepare(state); |
194 | 0 |
|
195 | 0 | console_start_sync(); |
196 | 0 | printk("Entering ACPI S%d state.\n", state); |
197 | 0 |
|
198 | 0 | local_irq_save(flags); |
199 | 0 | spin_debug_disable(); |
200 | 0 |
|
201 | 0 | if ( (error = device_power_down()) != SAVED_ALL ) |
202 | 0 | { |
203 | 0 | printk(XENLOG_ERR "Some devices failed to power down."); |
204 | 0 | system_state = SYS_STATE_resume; |
205 | 0 | device_power_up(error); |
206 | 0 | error = -EIO; |
207 | 0 | goto done; |
208 | 0 | } |
209 | 0 | else |
210 | 0 | error = 0; |
211 | 0 |
|
212 | 0 | ACPI_FLUSH_CPU_CACHE(); |
213 | 0 |
|
214 | 0 | switch ( state ) |
215 | 0 | { |
216 | 0 | case ACPI_STATE_S3: |
217 | 0 | do_suspend_lowlevel(); |
218 | 0 | system_reset_counter++; |
219 | 0 | error = tboot_s3_resume(); |
220 | 0 | break; |
221 | 0 | case ACPI_STATE_S5: |
222 | 0 | acpi_enter_sleep_state(ACPI_STATE_S5); |
223 | 0 | break; |
224 | 0 | default: |
225 | 0 | error = -EINVAL; |
226 | 0 | break; |
227 | 0 | } |
228 | 0 |
|
229 | 0 | system_state = SYS_STATE_resume; |
230 | 0 |
|
231 | 0 | /* Restore CR4 and EFER from cached values. */ |
232 | 0 | cr4 = read_cr4(); |
233 | 0 | write_cr4(cr4 & ~X86_CR4_MCE); |
234 | 0 | write_efer(read_efer()); |
235 | 0 |
|
236 | 0 | device_power_up(SAVED_ALL); |
237 | 0 |
|
238 | 0 | mcheck_init(&boot_cpu_data, false); |
239 | 0 | write_cr4(cr4); |
240 | 0 |
|
241 | 0 | printk(XENLOG_INFO "Finishing wakeup from ACPI S%d state.\n", state); |
242 | 0 |
|
243 | 0 | if ( (state == ACPI_STATE_S3) && error ) |
244 | 0 | tboot_s3_error(error); |
245 | 0 |
|
246 | 0 | done: |
247 | 0 | spin_debug_enable(); |
248 | 0 | local_irq_restore(flags); |
249 | 0 | console_end_sync(); |
250 | 0 | acpi_sleep_post(state); |
251 | 0 | if ( hvm_cpu_up() ) |
252 | 0 | BUG(); |
253 | 0 |
|
254 | 0 | enable_cpu: |
255 | 0 | cpufreq_add_cpu(0); |
256 | 0 | microcode_resume_cpu(0); |
257 | 0 | rcu_barrier(); |
258 | 0 | mtrr_aps_sync_begin(); |
259 | 0 | enable_nonboot_cpus(); |
260 | 0 | mtrr_aps_sync_end(); |
261 | 0 | adjust_vtd_irq_affinities(); |
262 | 0 | acpi_dmar_zap(); |
263 | 0 | thaw_domains(); |
264 | 0 | system_state = SYS_STATE_active; |
265 | 0 | spin_unlock(&pm_lock); |
266 | 0 | return error; |
267 | 0 | } |
268 | | |
269 | | static long enter_state_helper(void *data) |
270 | 0 | { |
271 | 0 | struct acpi_sleep_info *sinfo = (struct acpi_sleep_info *)data; |
272 | 0 | return enter_state(sinfo->sleep_state); |
273 | 0 | } |
274 | | |
275 | | /* |
276 | | * Dom0 issues this hypercall in place of writing pm1a_cnt. Xen then |
277 | | * takes over the control and put the system into sleep state really. |
278 | | */ |
279 | | int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep) |
280 | 0 | { |
281 | 0 | if ( sleep->flags & XENPF_ACPI_SLEEP_EXTENDED ) |
282 | 0 | { |
283 | 0 | if ( !acpi_sinfo.sleep_control.address || |
284 | 0 | !acpi_sinfo.sleep_status.address ) |
285 | 0 | return -EPERM; |
286 | 0 |
|
287 | 0 | if ( sleep->flags & ~XENPF_ACPI_SLEEP_EXTENDED ) |
288 | 0 | return -EINVAL; |
289 | 0 |
|
290 | 0 | if ( sleep->val_a > ACPI_SLEEP_TYPE_MAX || |
291 | 0 | (sleep->val_b != ACPI_SLEEP_TYPE_INVALID && |
292 | 0 | sleep->val_b > ACPI_SLEEP_TYPE_MAX) ) |
293 | 0 | return -ERANGE; |
294 | 0 |
|
295 | 0 | acpi_sinfo.sleep_type_a = sleep->val_a; |
296 | 0 | acpi_sinfo.sleep_type_b = sleep->val_b; |
297 | 0 |
|
298 | 0 | acpi_sinfo.sleep_extended = 1; |
299 | 0 | } |
300 | 0 |
|
301 | 0 | else if ( !acpi_sinfo.pm1a_cnt_blk.address ) |
302 | 0 | return -EPERM; |
303 | 0 |
|
304 | 0 | /* Sanity check */ |
305 | 0 | else if ( sleep->val_b && |
306 | 0 | ((sleep->val_a ^ sleep->val_b) & ACPI_BITMASK_SLEEP_ENABLE) ) |
307 | 0 | { |
308 | 0 | gdprintk(XENLOG_ERR, "Mismatched pm1a/pm1b setting\n"); |
309 | 0 | return -EINVAL; |
310 | 0 | } |
311 | 0 |
|
312 | 0 | else if ( sleep->flags ) |
313 | 0 | return -EINVAL; |
314 | 0 |
|
315 | 0 | else |
316 | 0 | { |
317 | 0 | acpi_sinfo.pm1a_cnt_val = sleep->val_a; |
318 | 0 | acpi_sinfo.pm1b_cnt_val = sleep->val_b; |
319 | 0 | acpi_sinfo.sleep_extended = 0; |
320 | 0 | } |
321 | 0 |
|
322 | 0 | acpi_sinfo.sleep_state = sleep->sleep_state; |
323 | 0 |
|
324 | 0 | return continue_hypercall_on_cpu(0, enter_state_helper, &acpi_sinfo); |
325 | 0 | } |
326 | | |
327 | | static int acpi_get_wake_status(void) |
328 | 0 | { |
329 | 0 | uint32_t val; |
330 | 0 | acpi_status status; |
331 | 0 |
|
332 | 0 | if ( acpi_sinfo.sleep_extended ) |
333 | 0 | { |
334 | 0 | status = acpi_hw_register_read(ACPI_REGISTER_SLEEP_STATUS, &val); |
335 | 0 |
|
336 | 0 | return ACPI_FAILURE(status) ? 0 : val & ACPI_X_WAKE_STATUS; |
337 | 0 | } |
338 | 0 |
|
339 | 0 | /* Wake status is the 15th bit of PM1 status register. (ACPI spec 3.0) */ |
340 | 0 | status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &val); |
341 | 0 | if ( ACPI_FAILURE(status) ) |
342 | 0 | return 0; |
343 | 0 |
|
344 | 0 | val &= ACPI_BITMASK_WAKE_STATUS; |
345 | 0 | val >>= ACPI_BITPOSITION_WAKE_STATUS; |
346 | 0 | return val; |
347 | 0 | } |
348 | | |
349 | | static void tboot_sleep(u8 sleep_state) |
350 | 0 | { |
351 | 0 | uint32_t shutdown_type; |
352 | 0 |
|
353 | 0 | #define TB_COPY_GAS(tbg, g) \ |
354 | 0 | tbg.space_id = g.space_id; \ |
355 | 0 | tbg.bit_width = g.bit_width; \ |
356 | 0 | tbg.bit_offset = g.bit_offset; \ |
357 | 0 | tbg.access_width = g.access_width; \ |
358 | 0 | tbg.address = g.address; |
359 | 0 |
|
360 | 0 | /* sizes are not same (due to packing) so copy each one */ |
361 | 0 | TB_COPY_GAS(g_tboot_shared->acpi_sinfo.pm1a_cnt_blk, |
362 | 0 | acpi_sinfo.pm1a_cnt_blk); |
363 | 0 | TB_COPY_GAS(g_tboot_shared->acpi_sinfo.pm1b_cnt_blk, |
364 | 0 | acpi_sinfo.pm1b_cnt_blk); |
365 | 0 | TB_COPY_GAS(g_tboot_shared->acpi_sinfo.pm1a_evt_blk, |
366 | 0 | acpi_sinfo.pm1a_evt_blk); |
367 | 0 | TB_COPY_GAS(g_tboot_shared->acpi_sinfo.pm1b_evt_blk, |
368 | 0 | acpi_sinfo.pm1b_evt_blk); |
369 | 0 | g_tboot_shared->acpi_sinfo.pm1a_cnt_val = acpi_sinfo.pm1a_cnt_val; |
370 | 0 | g_tboot_shared->acpi_sinfo.pm1b_cnt_val = acpi_sinfo.pm1b_cnt_val; |
371 | 0 | g_tboot_shared->acpi_sinfo.wakeup_vector = acpi_sinfo.wakeup_vector; |
372 | 0 | g_tboot_shared->acpi_sinfo.vector_width = acpi_sinfo.vector_width; |
373 | 0 | g_tboot_shared->acpi_sinfo.kernel_s3_resume_vector = |
374 | 0 | bootsym_phys(wakeup_start); |
375 | 0 |
|
376 | 0 | switch ( sleep_state ) |
377 | 0 | { |
378 | 0 | case ACPI_STATE_S3: |
379 | 0 | shutdown_type = TB_SHUTDOWN_S3; |
380 | 0 | break; |
381 | 0 | case ACPI_STATE_S4: |
382 | 0 | shutdown_type = TB_SHUTDOWN_S4; |
383 | 0 | break; |
384 | 0 | case ACPI_STATE_S5: |
385 | 0 | shutdown_type = TB_SHUTDOWN_S5; |
386 | 0 | break; |
387 | 0 | default: |
388 | 0 | return; |
389 | 0 | } |
390 | 0 |
|
391 | 0 | tboot_shutdown(shutdown_type); |
392 | 0 | } |
393 | | |
394 | | /* System is really put into sleep state by this stub */ |
395 | | acpi_status acpi_enter_sleep_state(u8 sleep_state) |
396 | 0 | { |
397 | 0 | acpi_status status; |
398 | 0 |
|
399 | 0 | if ( tboot_in_measured_env() ) |
400 | 0 | { |
401 | 0 | tboot_sleep(sleep_state); |
402 | 0 | printk(XENLOG_ERR "TBOOT failed entering s3 state\n"); |
403 | 0 | return_ACPI_STATUS(AE_ERROR); |
404 | 0 | } |
405 | 0 |
|
406 | 0 | ACPI_FLUSH_CPU_CACHE(); |
407 | 0 |
|
408 | 0 | if ( acpi_sinfo.sleep_extended ) |
409 | 0 | { |
410 | 0 | /* |
411 | 0 | * Set the SLP_TYP and SLP_EN bits. |
412 | 0 | * |
413 | 0 | * Note: We only use the first value returned by the \_Sx method |
414 | 0 | * (acpi_sinfo.sleep_type_a) - As per ACPI specification. |
415 | 0 | */ |
416 | 0 | u8 sleep_type_value = |
417 | 0 | ((acpi_sinfo.sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) & |
418 | 0 | ACPI_X_SLEEP_TYPE_MASK) | ACPI_X_SLEEP_ENABLE; |
419 | 0 |
|
420 | 0 | status = acpi_hw_register_write(ACPI_REGISTER_SLEEP_CONTROL, |
421 | 0 | sleep_type_value); |
422 | 0 | } |
423 | 0 | else |
424 | 0 | { |
425 | 0 | status = acpi_hw_register_write(ACPI_REGISTER_PM1A_CONTROL, |
426 | 0 | acpi_sinfo.pm1a_cnt_val); |
427 | 0 | if ( !ACPI_FAILURE(status) && acpi_sinfo.pm1b_cnt_blk.address ) |
428 | 0 | status = acpi_hw_register_write(ACPI_REGISTER_PM1B_CONTROL, |
429 | 0 | acpi_sinfo.pm1b_cnt_val); |
430 | 0 | } |
431 | 0 |
|
432 | 0 | if ( ACPI_FAILURE(status) ) |
433 | 0 | return_ACPI_STATUS(AE_ERROR); |
434 | 0 |
|
435 | 0 | /* Wait until we enter sleep state, and spin until we wake */ |
436 | 0 | while ( !acpi_get_wake_status() ) |
437 | 0 | continue; |
438 | 0 |
|
439 | 0 | return_ACPI_STATUS(AE_OK); |
440 | 0 | } |
441 | | |
442 | | static int __init acpi_sleep_init(void) |
443 | 1 | { |
444 | 1 | int i; |
445 | 1 | char *p = opt_acpi_sleep; |
446 | 1 | |
447 | 1 | while ( (p != NULL) && (*p != '\0') ) |
448 | 0 | { |
449 | 0 | if ( !strncmp(p, "s3_bios", 7) ) |
450 | 0 | acpi_video_flags |= 1; |
451 | 0 | if ( !strncmp(p, "s3_mode", 7) ) |
452 | 0 | acpi_video_flags |= 2; |
453 | 0 | p = strchr(p, ','); |
454 | 0 | if ( p != NULL ) |
455 | 0 | p += strspn(p, ", \t"); |
456 | 0 | } |
457 | 1 | |
458 | 1 | printk(XENLOG_INFO "ACPI sleep modes:"); |
459 | 7 | for ( i = 0; i < ACPI_S_STATE_COUNT; i++ ) |
460 | 6 | { |
461 | 6 | if ( i == ACPI_STATE_S3 ) |
462 | 1 | { |
463 | 1 | sleep_states[i] = 1; |
464 | 1 | printk(" S%d", i); |
465 | 1 | } |
466 | 6 | else |
467 | 5 | sleep_states[i] = 0; |
468 | 6 | } |
469 | 1 | printk("\n"); |
470 | 1 | |
471 | 1 | return 0; |
472 | 1 | } |
473 | | __initcall(acpi_sleep_init); |