summaryrefslogtreecommitdiff
path: root/arch/x86/lib/realmode_switch.S
blob: c4c4c4378e94a140094ab2bf233dd24d218d0148 (plain)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * (C) Copyright 2002
 * Daniel Engström, Omicron Ceti AB, daniel@omicron.se
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

/* 32bit -> 16bit -> 32bit mode switch code */

/*
 * Stack frame at 0xe00
 *	e00 ebx;
 *	e04 ecx;
 *	e08 edx;
 *	e0c esi;
 *	e10 edi;
 *	e14 ebp;
 *	e18 eax;
 *	e1c ds;
 *	e20 es;
 *	e24 fs;
 *	e28 gs;
 *	e2c orig_eax;
 *	e30 eip;
 *	e34 cs;
 *	e38 eflags;
 *	e3c esp;
 *	e40 ss;
 */

#define a32		.byte 0x67;	/* address size prefix 32 */
#define o32		.byte 0x66;	/* operand size prefix 32 */

.section .realmode, "ax"
.code16

	/* 16bit protected mode code here */
.globl realmode_enter
realmode_enter:
o32	pusha
o32	pushf
	cli
	sidt	saved_idt
	sgdt	saved_gdt
	movl	%esp, %eax
	movl	%eax, saved_protected_mode_esp

	movl	$0x10, %eax
	movl	%eax, %esp
	movw	$0x28, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs

	lidt	realmode_idt_ptr
	/* Go back into real mode by clearing PE to 0 */
	movl	%cr0, %eax
	andl	$0x7ffffffe, %eax
	movl	%eax, %cr0

	/* switch to real mode */
	ljmp	$0x0,$do_realmode

do_realmode:
	/* realmode code from here */
	movw	%cs,%ax
	movw	%ax,%ds
	movw	%ax,%es
	movw	%ax,%fs
	movw	%ax,%gs

	/* create a temporary stack */
	movw	$0xc0, %ax
	movw	%ax, %ss
	movw	$0x200, %ax
	movw	%ax, %sp

	popl	%ebx
	popl	%ecx
	popl	%edx
	popl	%esi
	popl	%edi
	popl	%ebp
	popl	%eax
	movl	%eax, temp_eax
	popl	%eax
	movw	%ax, %ds
	popl	%eax
	movw	%ax, %es
	popl	%eax
	movw	%ax, %fs
	popl	%eax
	movw	%ax, %gs
	popl	%eax				/* orig_eax */
	popl	%eax
cs	movw	%ax, temp_ip
	popl	%eax
cs	movw	%ax, temp_cs
o32	popf
	popl	%eax
	popw	%ss
	movl	%eax, %esp
cs	movl	temp_eax, %eax

	/* self-modifying code, better flush the cache */
	wbinvd

	.byte	0x9a				/* lcall */
temp_ip:
	.word	0				/* new ip */
temp_cs:
	.word	0				/* new cs */

realmode_ret:
	/* save eax, esp and ss */
cs	movl	%eax, saved_eax
	movl	%esp, %eax
cs	movl	%eax, saved_esp
	movw	%ss, %ax
cs	movw	%ax, saved_ss

	/*
	 * restore the stack, note that we set sp to 0x244;
	 * pt_regs is 0x44 bytes long and we push the structure
	 * backwards on to the stack, bottom first
	 */
	movw	$0xc0, %ax
	movw	%ax, %ss
	movw	$0x244, %ax
	movw	%ax, %sp

	xorl	%eax,%eax
cs	movw	saved_ss, %ax
	pushl	%eax
cs	movl	saved_esp, %eax
	pushl	%eax
o32	pushf
	xorl	%eax,%eax
cs	movw	temp_cs, %ax
	pushl	%eax
cs	movw	temp_ip, %ax
	pushl	%eax
	pushl	$0
	movw	%gs, %ax
	pushl	%eax
	movw	%fs, %ax
	pushl	%eax
	movw	%es, %ax
	pushl	%eax
	movw	%ds, %ax
	pushl	%eax
	movl	saved_eax, %eax
	pushl	%eax
	pushl	%ebp
	pushl	%edi
	pushl	%esi
	pushl	%edx
	pushl	%ecx
	pushl	%ebx

o32 cs	lidt	saved_idt
o32 cs	lgdt	saved_gdt

	/* Go back into protected mode reset PE to 1 */
	movl	%cr0, %eax
	orl	$1,%eax
	movl	%eax, %cr0

	/* flush prefetch queue */
	jmp	next_line
next_line:
	movw	$return_ptr, %ax
	movw	%ax,%bp
o32 cs	ljmp	*(%bp)

.code32
protected_mode:
	/* Reload segment registers */
	movl	$0x18, %eax
	movw	%ax, %fs
	movw	%ax, %ds
	movw	%ax, %gs
	movw	%ax, %es
	movw	%ax, %ss
	movl	saved_protected_mode_esp, %eax
	movl	%eax, %esp
	popf
	popa
	ret

temp_eax:
	.long	0

saved_ss:
	.word	0
saved_esp:
	.long	0
saved_eax:
	.long	0

realmode_idt_ptr:
	.word	0x400
	.word	0x0, 0x0

saved_gdt:
	.word	0, 0, 0, 0
saved_idt:
	.word	0, 0, 0, 0

saved_protected_mode_esp:
	.long	0

return_ptr:
	.long	protected_mode
	.word	0x10