Skip to content

Commit 05a0c6f

Browse files
ramesh-thomasnashif
authored andcommitted
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 <[email protected]>
1 parent 9471d1f commit 05a0c6f

File tree

3 files changed

+48
-71
lines changed

3 files changed

+48
-71
lines changed

arch/x86/soc/intel_quark/quark_se/power.c

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ uint64_t _pm_save_gdtr;
3030
uint64_t _pm_save_idtr;
3131
uint32_t _pm_save_esp;
3232

33+
extern void _power_soc_sleep(void);
34+
extern void _power_restore_cpu_context(void);
35+
extern void _power_soc_deep_sleep(void);
36+
3337
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
3438
static uint32_t *__x86_restore_info = (uint32_t *)CONFIG_BSP_SHARED_RAM_ADDR;
3539

3640
static void _deep_sleep(enum power_states state)
3741
{
38-
int restore;
39-
40-
__asm__ volatile ("wbinvd");
41-
4242
/*
4343
* Setting resume vector inside the restore_cpu_context
4444
* function since we have nothing to do before cpu context
@@ -47,22 +47,20 @@ static void _deep_sleep(enum power_states state)
4747
* can be done before cpu context is restored and control
4848
* transferred to _sys_soc_suspend.
4949
*/
50-
qm_x86_set_resume_vector(_sys_soc_restore_cpu_context,
50+
qm_x86_set_resume_vector(_power_restore_cpu_context,
5151
*__x86_restore_info);
5252

53-
restore = _sys_soc_save_cpu_context();
53+
power_soc_set_x86_restore_flag();
5454

55-
if (!restore) {
56-
power_soc_set_x86_restore_flag();
57-
58-
switch (state) {
59-
case SYS_POWER_STATE_DEEP_SLEEP_1:
60-
power_soc_sleep();
61-
case SYS_POWER_STATE_DEEP_SLEEP:
62-
power_soc_deep_sleep();
63-
default:
64-
break;
65-
}
55+
switch (state) {
56+
case SYS_POWER_STATE_DEEP_SLEEP_1:
57+
_power_soc_sleep();
58+
break;
59+
case SYS_POWER_STATE_DEEP_SLEEP:
60+
_power_soc_deep_sleep();
61+
break;
62+
default:
63+
break;
6664
}
6765
}
6866
#endif

arch/x86/soc/intel_quark/quark_se/soc_power.S

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,49 +23,57 @@ GDATA(_pm_save_gdtr)
2323
GDATA(_pm_save_idtr)
2424
GDATA(_pm_save_esp)
2525

26-
GTEXT(_sys_soc_save_cpu_context)
27-
GTEXT(_sys_soc_restore_cpu_context)
2826
GTEXT(_sys_soc_resume_from_deep_sleep)
27+
GTEXT(_power_restore_cpu_context)
28+
GTEXT(_power_soc_sleep)
29+
GTEXT(_power_soc_deep_sleep)
30+
31+
SECTION_FUNC(TEXT, save_cpu_context)
32+
movl %esp, %eax /* save ptr to return address */
2933

30-
SECTION_FUNC(TEXT, _sys_soc_save_cpu_context)
31-
movl %esp, %eax /* save ptr to return address */
3234
pushf /* save flags */
3335
pusha /* save GPRs */
34-
3536
movl %esp, _pm_save_esp /* save stack ptr */
3637
sidtl _pm_save_idtr /* save idtr */
3738
sgdtl _pm_save_gdtr /* save gdtr */
3839

39-
pushl (%eax) /* push return address */
40-
41-
xorl %eax, %eax /* 0 indicates saved context */
40+
pushl (%eax) /* push return address */
4241
ret
4342

44-
SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context)
45-
/*
46-
* Will transfer control to _sys_power_save_cpu_context,
47-
* from where it will return 1 indicating the function
48-
* is exiting after a context switch.
49-
*/
43+
SECTION_FUNC(TEXT, _power_restore_cpu_context)
5044
lgdtl _pm_save_gdtr /* restore gdtr */
5145
lidtl _pm_save_idtr /* restore idtr */
5246
movl _pm_save_esp, %esp /* restore saved stack ptr */
5347
popa /* restore saved GPRs */
5448
popf /* restore saved flags */
5549

5650
/*
57-
* At this point context is restored as it was saved
58-
* in _sys_soc_save_cpu_context. The following ret
59-
* will emulate a return from that function. Move 1
60-
* to eax to emulate a return 1. The caller of
61-
* _sys_soc_save_cpu_context will identify it is
62-
* returning from a context restore based on the
63-
* return value = 1.
51+
* At this point the stack contents will be as follows:
52+
*
53+
* Saved context
54+
* ESP ---> Return address of save_cpu_context
55+
* Return address of _power_soc_sleep/deep_sleep
56+
*
57+
* We just popped the saved context. Next we pop out the address
58+
* of the caller of save_cpu_context.Then the ret would return
59+
* to caller of _power_soc_sleep or _power_soc_deep_sleep.
60+
*
6461
*/
65-
xorl %eax, %eax
66-
incl %eax
62+
addl $4, %esp
6763
ret
6864

65+
SECTION_FUNC(TEXT, _power_soc_sleep)
66+
call save_cpu_context
67+
wbinvd
68+
call power_soc_sleep
69+
/* Does not return */
70+
71+
SECTION_FUNC(TEXT, _power_soc_deep_sleep)
72+
call save_cpu_context
73+
wbinvd
74+
call power_soc_deep_sleep
75+
/* Does not return */
76+
6977
/*
7078
* This is an example function to handle the deep sleep resume notification
7179
* in the absence of bootloader context restore support.
@@ -78,8 +86,8 @@ SECTION_FUNC(TEXT, _sys_soc_restore_cpu_context)
7886
*/
7987
SECTION_FUNC(TEXT, _sys_soc_resume_from_deep_sleep)
8088
movl $CONFIG_BSP_SHARED_RAM_ADDR, %eax
81-
cmpl $_sys_soc_restore_cpu_context, (%eax)
82-
je _sys_soc_restore_cpu_context
89+
cmpl $_power_restore_cpu_context, (%eax)
90+
je _power_restore_cpu_context
8391
ret
8492

8593
#endif

arch/x86/soc/intel_quark/quark_se/soc_power.h

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,35 +30,6 @@ enum power_states {
3030
SYS_POWER_STATE_MAX
3131
};
3232

33-
/**
34-
* @brief Save CPU context
35-
*
36-
* This function would save the CPU context in the stack. It
37-
* would also save the idtr and gdtr registers. When context is
38-
* restored by _sys_soc_restore_cpu_context(), control will be
39-
* transferred into this function where the context was originally
40-
* saved. The return values would indicate whether it is returning
41-
* after saving context or after a context restore transferred
42-
* control to it.
43-
*
44-
* @retval 0 Indicates it is returning after saving cpu context
45-
* @retval 1 Indicates cpu context restore transferred control to it.
46-
*/
47-
int _sys_soc_save_cpu_context(void);
48-
49-
/**
50-
* @brief Restore CPU context
51-
*
52-
* This function would restore the CPU context that was saved in
53-
* the stack by _sys_soc_save_cpu_context(). It would also restore
54-
* the idtr and gdtr registers.
55-
*
56-
* After context is restored, control will be transferred into
57-
* _sys_soc_save_cpu_context() function where the context was originally
58-
* saved.
59-
*/
60-
FUNC_NORETURN void _sys_soc_restore_cpu_context(void);
61-
6233
/**
6334
* @brief Put processor into low power state
6435
*

0 commit comments

Comments
 (0)