Ejemplo n.º 1
0
/*
 * Do not allocate memory (or fail in any way) in machine_kexec().
 * We are past the point of no return, committed to rebooting now.
 */
void machine_kexec(struct kimage *image)
{
	unsigned long page_list[PAGES_NR];
	void *control_page;
	int save_ftrace_enabled;
	asmlinkage unsigned long
		(*relocate_kernel_ptr)(unsigned long indirection_page,
				       unsigned long control_page,
				       unsigned long start_address,
				       unsigned int has_pae,
				       unsigned int preserve_context);

#ifdef CONFIG_KEXEC_JUMP
	if (image->preserve_context)
		save_processor_state();
#endif

	save_ftrace_enabled = __ftrace_enabled_save();

	/* Interrupts aren't acceptable while we reboot */
	local_irq_disable();

	if (image->preserve_context) {
#ifdef CONFIG_X86_IO_APIC
		/*
		 * We need to put APICs in legacy mode so that we can
		 * get timer interrupts in second kernel. kexec/kdump
		 * paths already have calls to disable_IO_APIC() in
		 * one form or other. kexec jump path also need
		 * one.
		 */
		disable_IO_APIC();
#endif
	}

	control_page = page_address(image->control_code_page);
	memcpy(control_page, relocate_kernel, KEXEC_CONTROL_CODE_MAX_SIZE);

	relocate_kernel_ptr = control_page;
	page_list[PA_CONTROL_PAGE] = __pa(control_page);
	page_list[VA_CONTROL_PAGE] = (unsigned long)control_page;
	page_list[PA_PGD] = __pa(image->arch.pgd);

	if (image->type == KEXEC_TYPE_DEFAULT)
		page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page)
						<< PAGE_SHIFT);

	/* now call it */
	image->start = relocate_kernel_ptr((unsigned long)image->head,
					   (unsigned long)page_list,
					   image->start, cpu_has_pae,
					   image->preserve_context);

#ifdef CONFIG_KEXEC_JUMP
	if (image->preserve_context)
		restore_processor_state();
#endif

	__ftrace_enabled_restore(save_ftrace_enabled);
}
Ejemplo n.º 2
0
int swsusp_suspend(void)
{
	int error;

	if ((error = arch_prepare_suspend()))
		return error;
	local_irq_disable();
	/* At this point, device_suspend() has been called, but *not*
	 * device_power_down(). We *must* device_power_down() now.
	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
	 * become desynchronized with the actual state of the hardware
	 * at resume time, and evil weirdness ensues.
	 */
	if ((error = device_power_down(PMSG_FREEZE))) {
		printk(KERN_ERR "Some devices failed to power down, aborting suspend\n");
		goto Enable_irqs;
	}

	if ((error = save_highmem())) {
		printk(KERN_ERR "swsusp: Not enough free pages for highmem\n");
		goto Restore_highmem;
	}

	save_processor_state();
	if ((error = swsusp_arch_suspend()))
		printk(KERN_ERR "Error %d suspending\n", error);
	/* Restore control flow magically appears here */
	restore_processor_state();
Restore_highmem:
	restore_highmem();
	device_power_up();
Enable_irqs:
	local_irq_enable();
	return error;
}
Ejemplo n.º 3
0
int swsusp_resume(void)
{
	int error;

	local_irq_disable();
	/* NOTE:  device_power_down() is just a suspend() with irqs off;
	 * it has no special "power things down" semantics
	 */
	if (device_power_down(PMSG_PRETHAW))
		printk(KERN_ERR "Some devices failed to power down, very bad\n");
	/* We'll ignore saved state, but this gets preempt count (etc) right */
	save_processor_state();
	error = restore_highmem();
	if (!error) {
		error = swsusp_arch_resume();
		/* The code below is only ever reached in case of a failure.
		 * Otherwise execution continues at place where
		 * swsusp_arch_suspend() was called
        	 */
		BUG_ON(!error);
		/* This call to restore_highmem() undos the previous one */
		restore_highmem();
	}
	/* The only reason why swsusp_arch_resume() can fail is memory being
	 * very tight, so we have to free it as soon as we can to avoid
	 * subsequent failures
	 */
	swsusp_free();
	restore_processor_state();
	touch_softlockup_watchdog();
	device_power_up();
	local_irq_enable();
	return error;
}
Ejemplo n.º 4
0
int swsusp_resume(void)
{
	int error;
	local_irq_disable();
	if (device_power_down(PMSG_FREEZE))
		printk(KERN_ERR "Some devices failed to power down, very bad\n");
	/* We'll ignore saved state, but this gets preempt count (etc) right */
	save_processor_state();
	error = swsusp_arch_resume();
	/* Code below is only ever reached in case of failure. Otherwise
	 * execution continues at place where swsusp_arch_suspend was called
         */
	BUG_ON(!error);
	/* The only reason why swsusp_arch_resume() can fail is memory being
	 * very tight, so we have to free it as soon as we can to avoid
	 * subsequent failures
	 */
	swsusp_free();
	restore_processor_state();
	restore_highmem();
	touch_softlockup_watchdog();
	device_power_up();
	local_irq_enable();
	return error;
}
Ejemplo n.º 5
0
int swsusp_suspend(void)
{
	int error;
	if ((error = arch_prepare_suspend()))
		return error;
	local_irq_disable();
	/* At this point, device_suspend() has been called, but *not*
	 * device_power_down(). We *must* device_power_down() now.
	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
	 * become desynchronized with the actual state of the hardware
	 * at resume time, and evil weirdness ensues.
	 */
	if ((error = device_power_down(PMSG_FREEZE))) {
		local_irq_enable();
		return error;
	}
	save_processor_state();
	error = swsusp_arch_suspend();
	/* Restore control flow magically appears here */
	restore_processor_state();
	restore_highmem();
	device_power_up();
	local_irq_enable();
	return error;
}
Ejemplo n.º 6
0
int __init pmdisk_restore(void)
{
	int error;
	local_irq_disable();
	save_processor_state();
	error = pmdisk_arch_suspend(1);
	restore_processor_state();
	local_irq_enable();
	return error;
}
Ejemplo n.º 7
0
static int create_image(int platform_mode)
{
	int error;

	error = arch_prepare_suspend();
	if (error)
		return error;

	device_pm_lock();
	local_irq_disable();
	/* At this point, device_suspend() has been called, but *not*
	 * device_power_down(). We *must* call device_power_down() now.
	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
	 * become desynchronized with the actual state of the hardware
	 * at resume time, and evil weirdness ensues.
	 */
	error = device_power_down(PMSG_FREEZE);
	if (error) {
		printk(KERN_ERR "PM: Some devices failed to power down, "
			"aborting hibernation\n");
		goto Enable_irqs;
	}

	if (hibernation_test(TEST_CORE))
		goto Power_up;

	in_suspend = 1;
	save_processor_state();
	error = swsusp_arch_suspend();
	if (error)
		printk(KERN_ERR "PM: Error %d creating hibernation image\n",
			error);
	/* Restore control flow magically appears here */
	restore_processor_state();
	if (!in_suspend)
		platform_leave(platform_mode);
 Power_up:
	/* NOTE:  device_power_up() is just a resume() for devices
	 * that suspended with irqs off ... no overall powerup.
	 */
	device_power_up(in_suspend ?
		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
 Enable_irqs:
	local_irq_enable();
	device_pm_unlock();
	return error;
}
Ejemplo n.º 8
0
int pmdisk_save(void) 
{
	int error;

#if defined (CONFIG_HIGHMEM) || defined (CONFIG_DISCONTIGMEM)
	pr_debug("pmdisk: not supported with high- or discontig-mem.\n");
	return -EPERM;
#endif
	if ((error = arch_prepare_suspend()))
		return error;
	local_irq_disable();
	save_processor_state();
	error = pmdisk_arch_suspend(0);
	restore_processor_state();
	local_irq_enable();
	return error;
}
Ejemplo n.º 9
0
int swsusp_resume(void)
{
	int error;
	local_irq_disable();
	device_power_down(PMSG_FREEZE);
	/* We'll ignore saved state, but this gets preempt count (etc) right */
	save_processor_state();
	error = swsusp_arch_resume();
	/* Code below is only ever reached in case of failure. Otherwise
	 * execution continues at place where swsusp_arch_suspend was called
         */
	BUG_ON(!error);
	restore_processor_state();
	restore_highmem();
	device_power_up();
	local_irq_enable();
	return error;
}
/**
 * toi_atomic_restore - prepare to do the atomic restore
 *
 * Get ready to do the atomic restore. This part gets us into the same
 * state we are in prior to do calling do_toi_lowlevel while
 * hibernating: hot-unplugging secondary cpus and freeze processes,
 * before starting the thread that will do the restore.
 **/
int toi_atomic_restore(void)
{
	int error;

	toi_running = 1;

	toi_prepare_status(DONT_CLEAR_BAR, "Atomic restore.");

	memcpy(&toi_bkd.toi_nosave_commandline, saved_command_line, strlen(saved_command_line));

	toi_pre_atomic_restore_modules(&toi_bkd);

	if (add_boot_kernel_data_pbe())
		goto Failed;

	toi_prepare_status(DONT_CLEAR_BAR, "Doing atomic copy/restore.");

	if (toi_go_atomic(PMSG_QUIESCE, 0))
		goto Failed;

	/* We'll ignore saved state, but this gets preempt count (etc) right */
	save_processor_state();

	error = swsusp_arch_resume();
	/*
	 * Code below is only ever reached in case of failure. Otherwise
	 * execution continues at place where swsusp_arch_suspend was called.
	 *
	 * We don't know whether it's safe to continue (this shouldn't happen),
	 * so lets err on the side of caution.
	 */
	BUG();

 Failed:
	free_pbe_list(&restore_pblist, 0);
#ifdef CONFIG_HIGHMEM
	pr_warn("[%s] 0x%p 0x%p 0x%p\n", __func__,
			restore_highmem_pblist->address, restore_highmem_pblist->orig_address, restore_highmem_pblist->next);
	if (restore_highmem_pblist->next != NULL)
		free_pbe_list(&restore_highmem_pblist, 1);
#endif
	toi_running = 0;
	return 1;
}
Ejemplo n.º 11
0
static int resume_target_kernel(void)
{
	int error;

	device_pm_lock();
	local_irq_disable();
	error = device_power_down(PMSG_QUIESCE);
	if (error) {
		printk(KERN_ERR "PM: Some devices failed to power down, "
			"aborting resume\n");
		goto Enable_irqs;
	}
	/* We'll ignore saved state, but this gets preempt count (etc) right */
	save_processor_state();
	error = restore_highmem();
	if (!error) {
		error = swsusp_arch_resume();
		/*
		 * The code below is only ever reached in case of a failure.
		 * Otherwise execution continues at place where
		 * swsusp_arch_suspend() was called
		 */
		BUG_ON(!error);
		/* This call to restore_highmem() undos the previous one */
		restore_highmem();
	}
	/*
	 * The only reason why swsusp_arch_resume() can fail is memory being
	 * very tight, so we have to free it as soon as we can to avoid
	 * subsequent failures
	 */
	swsusp_free();
	restore_processor_state();
	touch_softlockup_watchdog();
	device_power_up(PMSG_RECOVER);
 Enable_irqs:
	local_irq_enable();
	device_pm_unlock();
	return error;
}
Ejemplo n.º 12
0
int create_image(int platform_mode)
{
	int error;

	error = arch_prepare_suspend();
	if (error)
		return error;

	local_irq_disable();
	/* At this point, device_suspend() has been called, but *not*
	 * device_power_down(). We *must* call device_power_down() now.
	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
	 * become desynchronized with the actual state of the hardware
	 * at resume time, and evil weirdness ensues.
	 */
	error = device_power_down(PMSG_FREEZE);
	if (error) {
		printk(KERN_ERR "Some devices failed to power down, "
			KERN_ERR "aborting suspend\n");
		goto Enable_irqs;
	}

	save_processor_state();
	error = swsusp_arch_suspend();
	if (error)
		printk(KERN_ERR "Error %d while creating the image\n", error);
	/* Restore control flow magically appears here */
	restore_processor_state();
	if (!in_suspend)
		platform_leave(platform_mode);
	/* NOTE:  device_power_up() is just a resume() for devices
	 * that suspended with irqs off ... no overall powerup.
	 */
	device_power_up();
 Enable_irqs:
	local_irq_enable();
	return error;
}
Ejemplo n.º 13
0
/**
 * resume_target_kernel - Restore system state from a hibernation image.
 * @platform_mode: Whether or not to use the platform driver.
 *
 * Execute device drivers' "noirq" and "late" freeze callbacks, restore the
 * contents of highmem that have not been restored yet from the image and run
 * the low-level code that will restore the remaining contents of memory and
 * switch to the just restored target kernel.
 */
static int resume_target_kernel(bool platform_mode)
{
	int error;

	error = dpm_suspend_end(PMSG_QUIESCE);
	if (error) {
		pr_err("Some devices failed to power down, aborting resume\n");
		return error;
	}

	error = platform_pre_restore(platform_mode);
	if (error)
		goto Cleanup;

	error = hibernate_resume_nonboot_cpu_disable();
	if (error)
		goto Enable_cpus;

	local_irq_disable();

	error = syscore_suspend();
	if (error)
		goto Enable_irqs;

	save_processor_state();
	error = restore_highmem();
	if (!error) {
		error = swsusp_arch_resume();
		/*
		 * The code below is only ever reached in case of a failure.
		 * Otherwise, execution continues at the place where
		 * swsusp_arch_suspend() was called.
		 */
		BUG_ON(!error);
		/*
		 * This call to restore_highmem() reverts the changes made by
		 * the previous one.
		 */
		restore_highmem();
	}
	/*
	 * The only reason why swsusp_arch_resume() can fail is memory being
	 * very tight, so we have to free it as soon as we can to avoid
	 * subsequent failures.
	 */
	swsusp_free();
	restore_processor_state();
	touch_softlockup_watchdog();

	syscore_resume();

 Enable_irqs:
	local_irq_enable();

 Enable_cpus:
	enable_nonboot_cpus();

 Cleanup:
	platform_restore_cleanup(platform_mode);

	dpm_resume_start(PMSG_RECOVER);

	return error;
}
Ejemplo n.º 14
0
/**
 * create_image - Create a hibernation image.
 * @platform_mode: Whether or not to use the platform driver.
 *
 * Execute device drivers' "late" and "noirq" freeze callbacks, create a
 * hibernation image and run the drivers' "noirq" and "early" thaw callbacks.
 *
 * Control reappears in this routine after the subsequent restore.
 */
static int create_image(int platform_mode)
{
	int error;

	error = dpm_suspend_end(PMSG_FREEZE);
	if (error) {
		pr_err("Some devices failed to power down, aborting hibernation\n");
		return error;
	}

	error = platform_pre_snapshot(platform_mode);
	if (error || hibernation_test(TEST_PLATFORM))
		goto Platform_finish;

	error = disable_nonboot_cpus();
	if (error || hibernation_test(TEST_CPUS))
		goto Enable_cpus;

	local_irq_disable();

	error = syscore_suspend();
	if (error) {
		pr_err("Some system devices failed to power down, aborting hibernation\n");
		goto Enable_irqs;
	}

	if (hibernation_test(TEST_CORE) || pm_wakeup_pending())
		goto Power_up;

	in_suspend = 1;
	save_processor_state();
	trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true);
	error = swsusp_arch_suspend();
	/* Restore control flow magically appears here */
	restore_processor_state();
	trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false);
	if (error)
		pr_err("Error %d creating hibernation image\n", error);

	if (!in_suspend) {
		events_check_enabled = false;
		clear_free_pages();
	}

	platform_leave(platform_mode);

 Power_up:
	syscore_resume();

 Enable_irqs:
	local_irq_enable();

 Enable_cpus:
	enable_nonboot_cpus();

 Platform_finish:
	platform_finish(platform_mode);

	dpm_resume_start(in_suspend ?
		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);

	return error;
}
Ejemplo n.º 15
0
static int create_image(int platform_mode)
{
	int error;

	error = arch_prepare_suspend();
	if (error)
		return error;

	/* At this point, dpm_suspend_start() has been called, but *not*
	 * dpm_suspend_noirq(). We *must* call dpm_suspend_noirq() now.
	 * Otherwise, drivers for some devices (e.g. interrupt controllers)
	 * become desynchronized with the actual state of the hardware
	 * at resume time, and evil weirdness ensues.
	 */
	error = dpm_suspend_noirq(PMSG_FREEZE);
	if (error) {
		printk(KERN_ERR "PM: Some devices failed to power down, "
			"aborting hibernation\n");
		return error;
	}

	error = platform_pre_snapshot(platform_mode);
	if (error || hibernation_test(TEST_PLATFORM))
		goto Platform_finish;

	error = disable_nonboot_cpus();
	if (error || hibernation_test(TEST_CPUS)
	    || hibernation_testmode(HIBERNATION_TEST))
		goto Enable_cpus;

	local_irq_disable();

	error = sysdev_suspend(PMSG_FREEZE);
	if (error) {
		printk(KERN_ERR "PM: Some system devices failed to power down, "
			"aborting hibernation\n");
		goto Enable_irqs;
	}

	if (hibernation_test(TEST_CORE) || !pm_check_wakeup_events())
		goto Power_up;

	in_suspend = 1;
	save_processor_state();
	error = swsusp_arch_suspend();
	if (error)
		printk(KERN_ERR "PM: Error %d creating hibernation image\n",
			error);
	/* Restore control flow magically appears here */
	restore_processor_state();
	if (!in_suspend) {
		events_check_enabled = false;
		platform_leave(platform_mode);
	}

 Power_up:
	sysdev_resume();
	/* NOTE:  dpm_resume_noirq() is just a resume() for devices
	 * that suspended with irqs off ... no overall powerup.
	 */

 Enable_irqs:
	local_irq_enable();

 Enable_cpus:
	enable_nonboot_cpus();

 Platform_finish:
	platform_finish(platform_mode);

	dpm_resume_noirq(in_suspend ?
		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);

	return error;
}
Ejemplo n.º 16
0
/*
*********************************************************************************************************
*                           aw_early_suspend
*
*Description: prepare necessary info for suspend&resume;
*
*Return     : return 0 is process successed;
*
*Notes      : -1: data is ok;
*           -2: data has been destory.
*********************************************************************************************************
*/
static int aw_early_suspend(void)
{
#define MAX_RETRY_TIMES (5)

    __s32 retry = MAX_RETRY_TIMES;
    
    //backup device state
    mem_ccu_save((__ccmu_reg_list_t *)(SW_VA_CCM_IO_BASE));
    mem_gpio_save(&(saved_gpio_state));
    mem_tmr_save(&(saved_tmr_state));
    mem_twi_save(&(saved_twi_state));
    mem_sram_save(&(saved_sram_state));    

    if (likely(mem_para_info.axp_enable))
    {
        //backup volt and freq state, after backup device state
        mem_twi_init(AXP_IICBUS);
        /* backup voltages */
        while(-1 == (mem_para_info.suspend_dcdc2 = mem_get_voltage(POWER_VOL_DCDC2)) && --retry){
            ;
        }
        if(0 == retry){
            print_call_info();
            return -1;
        }else{
            retry = MAX_RETRY_TIMES;
        }   
        
        while(-1 == (mem_para_info.suspend_dcdc3 = mem_get_voltage(POWER_VOL_DCDC3)) && --retry){
            ;
        }
        if(0 == retry){
            print_call_info();
            return -1;
        }else{
            retry = MAX_RETRY_TIMES;
        }   
    }
    else
    {
        mem_para_info.suspend_dcdc2 = -1;
        mem_para_info.suspend_dcdc3 = -1;
    }
    printk("dcdc2:%d, dcdc3:%d\n", mem_para_info.suspend_dcdc2, mem_para_info.suspend_dcdc3);

    /*backup bus ratio*/
    mem_clk_getdiv(&mem_para_info.clk_div);
    /*backup pll ratio*/
    mem_clk_get_pll_factor(&mem_para_info.pll_factor);
    
    //backup mmu
    save_mmu_state(&(mem_para_info.saved_mmu_state));
    //backup cpu state
//    __save_processor_state(&(mem_para_info.saved_cpu_context));
    //backup 0x0000,0000 page entry, size?
//    save_mapping(MEM_SW_VA_SRAM_BASE);
//    mem_para_info.saved_cpu_context.ttb_1r = DRAM_STANDBY_PGD_PA;
    memcpy((void*)(DRAM_STANDBY_PGD_ADDR + 0x3000), (void*)0xc0007000, 0x1000);
	__cpuc_coherent_kern_range(DRAM_STANDBY_PGD_ADDR + 0x3000, DRAM_STANDBY_PGD_ADDR + 0x4000 - 1);
    mem_para_info.saved_mmu_state.ttb_1r = DRAM_STANDBY_PGD_PA;

    //prepare resume0 code for resume

    if((DRAM_BACKUP_SIZE1) < sizeof(mem_para_info)){
        //judge the reserved space for mem para is enough or not.
        print_call_info();
        return -1;
    }

    //clean all the data into dram
    memcpy((void *)DRAM_BACKUP_BASE_ADDR1, (void *)&mem_para_info, sizeof(mem_para_info));
    dmac_flush_range((void *)DRAM_BACKUP_BASE_ADDR1, (void *)(DRAM_BACKUP_BASE_ADDR1 + DRAM_BACKUP_SIZE1 - 1));

    //prepare dram training area data
    memcpy((void *)DRAM_BACKUP_BASE_ADDR2, (void *)DRAM_BASE_ADDR, DRAM_TRANING_SIZE);
    dmac_flush_range((void *)DRAM_BACKUP_BASE_ADDR2, (void *)(DRAM_BACKUP_BASE_ADDR2 + DRAM_BACKUP_SIZE2 - 1));
    
    mem_arch_suspend();
    save_processor_state(); 
    
    //before creating mapping, build the coherent between cache and memory
    __cpuc_flush_kern_all();
    __cpuc_coherent_kern_range(0xc0000000, 0xffffffff-1);
    //create 0x0000,0000 mapping table: 0x0000,0000 -> 0x0000,0000 
    //create_mapping();

    //clean and flush
    mem_flush_tlb();
    
#ifdef PRE_DISABLE_MMU
    //jump to sram: dram enter selfresh, and power off.
    mem = (super_standby_func)SRAM_FUNC_START_PA;
#else
    //jump to sram: dram enter selfresh, and power off.
    mem = (super_standby_func)SRAM_FUNC_START;
#endif
    //move standby code to sram
    memcpy((void *)SRAM_FUNC_START, (void *)&suspend_bin_start, (int)&suspend_bin_end - (int)&suspend_bin_start);

#ifdef CONFIG_AW_FPGA_PLATFORM
*(unsigned int *)(0xf0007000 - 0x4) = 0x12345678;
printk("%s,%d:%d\n",__FILE__,__LINE__, *(unsigned int *)(0xf0007000 - 0x4));
#endif 
#ifdef PRE_DISABLE_MMU
    //enable the mapping and jump
    //invalidate tlb? maybe, but now, at this situation,  0x0000 <--> 0x0000 mapping never stay in tlb before this.
    //busy_waiting();
    jump_to_suspend(mem_para_info.saved_mmu_state.ttb_1r, mem);
#else
    mem();
#endif

    return -2;

}
Ejemplo n.º 17
0
/**
 * create_image - Create a hibernation image.
 * @platform_mode: Whether or not to use the platform driver.
 *
 * Execute device drivers' .freeze_noirq() callbacks, create a hibernation image
 * and execute the drivers' .thaw_noirq() callbacks.
 *
 * Control reappears in this routine after the subsequent restore.
 */
static int create_image(int platform_mode)
{
	int error;

	error = dpm_suspend_noirq(PMSG_FREEZE);
	if (error) {
		printk(KERN_ERR "PM: Some devices failed to power down, "
			"aborting hibernation\n");
		return error;
	}

	error = platform_pre_snapshot(platform_mode);
	if (error || hibernation_test(TEST_PLATFORM))
		goto Platform_finish;

	error = disable_nonboot_cpus();
	if (error || hibernation_test(TEST_CPUS)
	    || hibernation_testmode(HIBERNATION_TEST))
		goto Enable_cpus;

	local_irq_disable();

	error = syscore_suspend();
	if (error) {
		printk(KERN_ERR "PM: Some system devices failed to power down, "
			"aborting hibernation\n");
		goto Enable_irqs;
	}

	if (hibernation_test(TEST_CORE) || pm_wakeup_pending())
		goto Power_up;

	in_suspend = 1;
	save_processor_state();
	error = swsusp_arch_suspend();
	if (error)
		printk(KERN_ERR "PM: Error %d creating hibernation image\n",
			error);
	/* Restore control flow magically appears here */
	restore_processor_state();
	if (!in_suspend) {
		events_check_enabled = false;
		platform_leave(platform_mode);
	}

 Power_up:
	syscore_resume();

 Enable_irqs:
	local_irq_enable();

 Enable_cpus:
	enable_nonboot_cpus();

 Platform_finish:
	platform_finish(platform_mode);

	dpm_resume_noirq(in_suspend ?
		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);

	return error;
}