quark_se: power_mgmt: Fixes a cpu context save bug
The cpu context save function was manipulating stack and returning to C caller. This can corrupt stack if the calling function has data saved and it pops before entering deep sleep. Moved sleep functions into assembly to avoid this. Jira: ZEP-1345 Change-Id: I8a6d279ec14e42424f764d9ce8cbbef32149fe84 Signed-off-by: Ramesh Thomas <ramesh.thomas@intel.com>
This commit is contained in:
parent
a100ada866
commit
4a09593714
3 changed files with 48 additions and 71 deletions
|
@ -30,15 +30,15 @@ uint64_t _pm_save_gdtr;
|
||||||
uint64_t _pm_save_idtr;
|
uint64_t _pm_save_idtr;
|
||||||
uint32_t _pm_save_esp;
|
uint32_t _pm_save_esp;
|
||||||
|
|
||||||
|
extern void _power_soc_sleep(void);
|
||||||
|
extern void _power_restore_cpu_context(void);
|
||||||
|
extern void _power_soc_deep_sleep(void);
|
||||||
|
|
||||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||||
static uint32_t *__x86_restore_info = (uint32_t *)CONFIG_BSP_SHARED_RAM_ADDR;
|
static uint32_t *__x86_restore_info = (uint32_t *)CONFIG_BSP_SHARED_RAM_ADDR;
|
||||||
|
|
||||||
static void _deep_sleep(enum power_states state)
|
static void _deep_sleep(enum power_states state)
|
||||||
{
|
{
|
||||||
int restore;
|
|
||||||
|
|
||||||
__asm__ volatile ("wbinvd");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setting resume vector inside the restore_cpu_context
|
* Setting resume vector inside the restore_cpu_context
|
||||||
* function since we have nothing to do before cpu context
|
* function since we have nothing to do before cpu context
|
||||||
|
@ -47,22 +47,20 @@ static void _deep_sleep(enum power_states state)
|
||||||
* can be done before cpu context is restored and control
|
* can be done before cpu context is restored and control
|
||||||
* transferred to _sys_soc_suspend.
|
* transferred to _sys_soc_suspend.
|
||||||
*/
|
*/
|
||||||
qm_x86_set_resume_vector(_sys_soc_restore_cpu_context,
|
qm_x86_set_resume_vector(_power_restore_cpu_context,
|
||||||
*__x86_restore_info);
|
*__x86_restore_info);
|
||||||
|
|
||||||
restore = _sys_soc_save_cpu_context();
|
power_soc_set_x86_restore_flag();
|
||||||
|
|
||||||
if (!restore) {
|
switch (state) {
|
||||||
power_soc_set_x86_restore_flag();
|
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
||||||
|
_power_soc_sleep();
|
||||||
switch (state) {
|
break;
|
||||||
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||||
power_soc_sleep();
|
_power_soc_deep_sleep();
|
||||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
break;
|
||||||
power_soc_deep_sleep();
|
default:
|
||||||
default:
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,30 +23,24 @@ GDATA(_pm_save_gdtr)
|
||||||
GDATA(_pm_save_idtr)
|
GDATA(_pm_save_idtr)
|
||||||
GDATA(_pm_save_esp)
|
GDATA(_pm_save_esp)
|
||||||
|
|
||||||
GTEXT(_sys_soc_save_cpu_context)
|
|
||||||
GTEXT(_sys_soc_restore_cpu_context)
|
|
||||||
GTEXT(_sys_soc_resume_from_deep_sleep)
|
GTEXT(_sys_soc_resume_from_deep_sleep)
|
||||||
|
GTEXT(_power_restore_cpu_context)
|
||||||
|
GTEXT(_power_soc_sleep)
|
||||||
|
GTEXT(_power_soc_deep_sleep)
|
||||||
|
|
||||||
|
SECTION_FUNC(TEXT, save_cpu_context)
|
||||||
|
movl %esp, %eax /* save ptr to return address */
|
||||||
|
|
||||||
SECTION_FUNC(TEXT, _sys_soc_save_cpu_context)
|
|
||||||
movl %esp, %eax /* save ptr to return address */
|
|
||||||
pushf /* save flags */
|
pushf /* save flags */
|
||||||
pusha /* save GPRs */
|
pusha /* save GPRs */
|
||||||
|
|
||||||
movl %esp, _pm_save_esp /* save stack ptr */
|
movl %esp, _pm_save_esp /* save stack ptr */
|
||||||
sidtl _pm_save_idtr /* save idtr */
|
sidtl _pm_save_idtr /* save idtr */
|
||||||
sgdtl _pm_save_gdtr /* save gdtr */
|
sgdtl _pm_save_gdtr /* save gdtr */
|
||||||
|
|
||||||
pushl (%eax) /* push return address */
|
pushl (%eax) /* push return address */
|
||||||
|
|
||||||
xorl %eax, %eax /* 0 indicates saved context */
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context)
|
SECTION_FUNC(TEXT, _power_restore_cpu_context)
|
||||||
/*
|
|
||||||
* Will transfer control to _sys_power_save_cpu_context,
|
|
||||||
* from where it will return 1 indicating the function
|
|
||||||
* is exiting after a context switch.
|
|
||||||
*/
|
|
||||||
lgdtl _pm_save_gdtr /* restore gdtr */
|
lgdtl _pm_save_gdtr /* restore gdtr */
|
||||||
lidtl _pm_save_idtr /* restore idtr */
|
lidtl _pm_save_idtr /* restore idtr */
|
||||||
movl _pm_save_esp, %esp /* restore saved stack ptr */
|
movl _pm_save_esp, %esp /* restore saved stack ptr */
|
||||||
|
@ -54,18 +48,32 @@ SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context)
|
||||||
popf /* restore saved flags */
|
popf /* restore saved flags */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At this point context is restored as it was saved
|
* At this point the stack contents will be as follows:
|
||||||
* in _sys_soc_save_cpu_context. The following ret
|
*
|
||||||
* will emulate a return from that function. Move 1
|
* Saved context
|
||||||
* to eax to emulate a return 1. The caller of
|
* ESP ---> Return address of save_cpu_context
|
||||||
* _sys_soc_save_cpu_context will identify it is
|
* Return address of _power_soc_sleep/deep_sleep
|
||||||
* returning from a context restore based on the
|
*
|
||||||
* return value = 1.
|
* We just popped the saved context. Next we pop out the address
|
||||||
|
* of the caller of save_cpu_context.Then the ret would return
|
||||||
|
* to caller of _power_soc_sleep or _power_soc_deep_sleep.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
xorl %eax, %eax
|
addl $4, %esp
|
||||||
incl %eax
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
SECTION_FUNC(TEXT, _power_soc_sleep)
|
||||||
|
call save_cpu_context
|
||||||
|
wbinvd
|
||||||
|
call power_soc_sleep
|
||||||
|
/* Does not return */
|
||||||
|
|
||||||
|
SECTION_FUNC(TEXT, _power_soc_deep_sleep)
|
||||||
|
call save_cpu_context
|
||||||
|
wbinvd
|
||||||
|
call power_soc_deep_sleep
|
||||||
|
/* Does not return */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is an example function to handle the deep sleep resume notification
|
* This is an example function to handle the deep sleep resume notification
|
||||||
* in the absence of bootloader context restore support.
|
* in the absence of bootloader context restore support.
|
||||||
|
@ -78,8 +86,8 @@ SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context)
|
||||||
*/
|
*/
|
||||||
SECTION_FUNC(TEXT, _sys_soc_resume_from_deep_sleep)
|
SECTION_FUNC(TEXT, _sys_soc_resume_from_deep_sleep)
|
||||||
movl $CONFIG_BSP_SHARED_RAM_ADDR, %eax
|
movl $CONFIG_BSP_SHARED_RAM_ADDR, %eax
|
||||||
cmpl $_sys_soc_restore_cpu_context, (%eax)
|
cmpl $_power_restore_cpu_context, (%eax)
|
||||||
je _sys_soc_restore_cpu_context
|
je _power_restore_cpu_context
|
||||||
ret
|
ret
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -30,35 +30,6 @@ enum power_states {
|
||||||
SYS_POWER_STATE_MAX
|
SYS_POWER_STATE_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Save CPU context
|
|
||||||
*
|
|
||||||
* This function would save the CPU context in the stack. It
|
|
||||||
* would also save the idtr and gdtr registers. When context is
|
|
||||||
* restored by _sys_soc_restore_cpu_context(), control will be
|
|
||||||
* transferred into this function where the context was originally
|
|
||||||
* saved. The return values would indicate whether it is returning
|
|
||||||
* after saving context or after a context restore transferred
|
|
||||||
* control to it.
|
|
||||||
*
|
|
||||||
* @retval 0 Indicates it is returning after saving cpu context
|
|
||||||
* @retval 1 Indicates cpu context restore transferred control to it.
|
|
||||||
*/
|
|
||||||
int _sys_soc_save_cpu_context(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Restore CPU context
|
|
||||||
*
|
|
||||||
* This function would restore the CPU context that was saved in
|
|
||||||
* the stack by _sys_soc_save_cpu_context(). It would also restore
|
|
||||||
* the idtr and gdtr registers.
|
|
||||||
*
|
|
||||||
* After context is restored, control will be transferred into
|
|
||||||
* _sys_soc_save_cpu_context() function where the context was originally
|
|
||||||
* saved.
|
|
||||||
*/
|
|
||||||
FUNC_NORETURN void _sys_soc_restore_cpu_context(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Put processor into low power state
|
* @brief Put processor into low power state
|
||||||
*
|
*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue