summaryrefslogtreecommitdiffstats
path: root/arch/x86/lib/traveler.S
blob: 113b19802eb972618f61dd3c84a54d73e2c00c95 (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
/*
 * Copyright (C) 2009 Juergen Beisert, Pengutronix
 *
 * Mostly stolen from the GRUB2 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.
 *
 *
 */

/**
 * @file
 * @brief Switch from the flat mode world into the real mode world and vice versa
 *
 * Note: These functions are *called* and return in a different operating mode
 */

/**
 * @fn void real_to_prot(void)
 * @brief Switch from temp. real mode back to flat mode
 *
 * Called from a 32 bit flat mode segment and returns into a 16 bit segment
 */

/**
 * @fn void prot_to_real(void)
 * @brief Switch from flat mode to real mode
 *
 * Called from a 16 bit real mode segment and returns into a 32 bit segment
 */

#include <asm/modes.h>

	.file "walkyrie.S"

/* keep the current flat mode stack pointer, while playing in real mode */
	.section .boot.data.protstack
	.code32
protstack: .long 4
/* temp. store */
return_addr: .long 4


	.section .boot.text.real_to_prot, "ax"
	.code16
	.globl	real_to_prot
	.type	real_to_prot, @function

/* Note: This routine should not change any other standard registers than eax */
real_to_prot:
	/*
	 * Always disable the interrupts, when returning to flat mode
	 */
	cli

	/* turn on protected mode */
	movl %cr0, %eax
	orl $0x00000001, %eax
	movl %eax, %cr0

	/* jump to relocation, flush prefetch queue, and reload %cs */
	DATA32 ljmp $__BOOT_CS, $return_to_flatmode
	.size real_to_prot, .-real_to_prot

/* ----------------------------------------------------------------------- */
	.section .boot.text.return_to_flatmode, "ax"
	.type return_to_flatmode, @function
	.code32

return_to_flatmode:
	/* reload other segment registers */
	movw $__BOOT_DS, %ax
	movw %ax, %ds
	movw %ax, %es
	movw %ax, %fs
	movw %ax, %gs
	movw %ax, %ss

	/* move the return address from the real mode to the flat mode stack */
	movl (%esp), %eax
	movl %eax, return_addr

	/* setup again the flat mode stack */
	movl protstack, %eax
	movl %eax, %esp
	movl %eax, %ebp

	movl return_addr, %eax
	movl %eax, (%esp)

	/* flag we returned happy here */
	xorl %eax, %eax
	ret
	.size return_to_flatmode, .-return_to_flatmode

/* ------------------------------------------------------------------------ */

/* Note: This routine should not change any other standard registers than eax */

	.section .boot.text.prot_to_real, "ax"
	.globl prot_to_real
	.type prot_to_real, @function
	.extern boot_stack
	.code32

prot_to_real:
	/* save the protected mode stack */
	movl %esp, %eax
	movl %eax, protstack

	/* prepare the real mode stack */
	/* - address to call to the top of this stack */
	movl (%esp), %eax
	movl %eax, boot_stack - 4

	/* - the stack itself */
	movl $boot_stack - 4, %eax
	movl %eax, %esp
	movl %eax, %ebp

	/* prepare segments limits to 16 bit */
	movw $__REAL_DS, %ax
	movw %ax, %ds
	movw %ax, %es
	movw %ax, %fs
	movw %ax, %gs
	movw %ax, %ss

	/* at last, also limit the code segment to 16 bit */
	ljmp $__REAL_CS, $return_to_realmode
	.size prot_to_real, .-prot_to_real

/* ----------------------------------------------------------------------- */

	.section .boot.text.return_to_realmode, "ax"
	.globl return_to_realmode
	.type return_to_realmode, @function
	.code16

return_to_realmode:
	/* disable protected mode */
	movl %cr0, %eax
	andl $(~0x00000001), %eax
	movl %eax, %cr0

	/*
	 * all the protected mode settings are still cached in the CPU.
	 * Refresh them by re-loading all registers in realmode
	 * Start with the CS, continue with the data registers
	 */
	ljmp $0, $enter_realmode

enter_realmode:
	xorl %eax, %eax
	movw %ax, %ds
	movw %ax, %es
	movw %ax, %fs
	movw %ax, %gs
	movw %ax, %ss
	/*
	 * back in plain real mode now, we can play again with the BIOS
	 */

	/* restore interrupts */
	sti

	/* return on realmode stack! */
	DATA32 ret

	.size return_to_realmode, .-return_to_realmode