1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
.globl entry, __switch_context, __exit_context, halt
.text
.align 4
/*
* Entry point
* We start execution from here.
* It is assumed that CPU is in 32-bit protected mode and
* all segments are 4GB and base zero (flat model).
*/
entry:
/* Save boot context and switch to our main context.
* Main context is statically defined in C.
*/
pushl %cs
call __switch_context
/* We get here when the main context switches back to
* the boot context.
* Return to previous bootloader.
*/
ret
/*
* Switch execution context
* This saves registers, segments, and GDT in the stack, then
* switches the stack, and restores everything from the new stack.
* This function takes no argument. New stack pointer is
* taken from global variable __context, and old stack pointer
* is also saved to __context. This way we can just jump to
* this routine to get back to the original context.
*
* Call this routine with lcall or pushl %cs; call.
*/
__switch_context:
/* Save everything in current stack */
pushfl /* 56 */
pushl %ds /* 52 */
pushl %es /* 48 */
pushl %fs /* 44 */
pushl %gs /* 40 */
pushal /* 8 */
subl $8, %esp
movw %ss, (%esp) /* 0 */
sgdt 2(%esp) /* 2 */
#if 0
/* Swap %cs and %eip on the stack, so lret will work */
movl 60(%esp), %eax
xchgl %eax, 64(%esp)
movl %eax, 60(%esp)
#endif
/* At this point we don't know if we are on flat segment
* or relocated. So compute the address offset from %eip.
* Assuming CS.base==DS.base==SS.base.
*/
call 1f
1: popl %ebx
subl $1b, %ebx
/* Interrupts are not allowed... */
cli
/* Current context pointer is our stack pointer */
movl %esp, %esi
/* Normalize the ctx pointer */
subl %ebx, %esi
/* Swap it with new value */
xchgl %esi, __context(%ebx)
/* Adjust new ctx pointer for current address offset */
addl %ebx, %esi
/* Load new %ss and %esp to temporary */
movzwl (%esi), %edx
movl 20(%esi), %eax
/* Load new GDT */
lgdt 2(%esi)
/* Load new stack segment with new GDT */
movl %edx, %ss
/* Set new stack pointer, but we have to adjust it because
* pushal saves %esp value before pushal, and we want the value
* after pushal.
*/
leal -32(%eax), %esp
/* Load the rest from new stack */
popal
popl %gs
popl %fs
popl %es
popl %ds
popfl
/* Finally, load new %cs and %eip */
lret
__exit_context:
/* Get back to the original context */
pushl %cs
call __switch_context
/* We get here if the other context attempt to switch to this
* dead context. This should not happen. */
halt:
cli
hlt
jmp halt
|