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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
|
#[1]Independent Software
[2][independent-software-logo.svg] [ ]
[3]About [4]Philosophy [5]Web Development [6]Portfolio [7]Blog
[8]osdev [9]asm
OPERATING SYSTEM DEVELOPMENT
JUMPING TO PROTECTED MODE
-
Oct 24, 2013
In the [10]previous section of this tutorial for writing your own toy
operating system, we discussed memory and focused on the 21st address
line (the "A20 line") that must be enabled before we can have access to
the full 4GB of memory, which is a prerequisite to entering protected
mode. Now it's time to jump to protected mode.
In fact, all we've done in the last few articles is prepare for
entering protected mode. We've set up a global descriptor table (GDT),
an interrupt descriptor table (IDT) and enabled the A20 line. All that
remains is actually jumping to protected mode where we'll finally be
able to execute 32-bit code so we can focus on our kernel.
This article is part of a series on toy operating system development.
[11]View the series index
[INS: :INS]
In the [12]previous section of this tutorial for writing your own toy
operating system, we discussed memory and focused on the 21st address
line (the "A20 line") that must be enabled before we can have access to
the full 4GB of memory, which is a prerequisite to entering protected
mode. Now it's time to jump to protected mode.
In fact, all we've done in the last few articles is prepare for
entering protected mode. We've set up a global descriptor table (GDT),
an interrupt descriptor table (IDT) and enabled the A20 line. All that
remains is actually jumping to protected mode where we'll finally be
able to execute 32-bit code so we can focus on our kernel.
This article is part of a series on toy operating system development.
[13]View the series index
Control registers
What we've seen so far while rolling our own first-stage and
second-stage boot loaders, is familiar processor registers: AX, BX, CX,
DX, segments like CS, DS, ES, SS, the instruction pointer IP and the
stack pointer SP. The 80386+ processors actually introduce some new
registers what will become important when we switch to 32-bit
programming.
For one thing, existing registers get wider. Where we used to have
access to AX (16 bits wide), we will soon have access to EAX (32-bits
wide), as well as EBX, ECX and EDX. We'll gain additional segment
registers as well (FS and GS). Similarly, the instruction pointer
becomes EIP (32 bits again) and so on. That's great, and requires no
great deal of explanation.
However, we gain other registers as well. The Intel 80386 processor
comes armed with a set of control registers, and we'll need one of them
to switch to protected mode so we might as well talk about it now.
These control registers change or control the behavior of the CPU. This
includes interrupt control, switching addressing mode, paging and
coprocessor control. The new registers are called CR0, CR1, CR2, CR3
and CR4.
The first control register, CR0, has various control flags that modify
the basic operation of the processor.
Bit Name Full name Description
31 PG Paging If 1, enable paging and use the CR3 register, else disable
paging
30 CD Cache disable Globally enables/disable the memory cache
29 NW Not-write through Globally enables/disable write-back caching
18 AM Alignment mask Alignment check enabled if AM set, AC flag (in
EFLAGS register) set, and privilege level is 3
16 WP Write protect Determines whether the CPU can write to pages
marked read-only
5 NE Numeric error Enable internal x87 floating point error reporting
when set, else enables PC style x87 error detection
4 ET Extension type On the 386, it allowed to specify whether the
external math coprocessor was an 80287 or 80387
3 TS Task switched Allows saving x87 task context upon a task switch
only after x87 instruction used
2 EM Emulation If set, no x87 floating point unit present, if clear,
x87 FPU present
1 MP Monitor co-processor Controls interaction of WAIT/FWAIT
instructions with TS flag in CR0
0 PE Protected mode enable If 1, system is in protected mode, else
system is in real mode
Some of these bits will become important for us later on, but for now,
we're interested in the very first bit: the PE bit. It enables
protected mode.
Switching to protected mode
In order to make the switch to protected mode, all we have to do is
enable the PE-bit in the CR0 register, like so:
.macro mGoProtected
mov eax, cr0
or eax, 1
mov cr0, eax
.endm
Clearing the prefetch queue
By setting the PE bit in the CR0 register, we have just switched to
protected mode. This means that all instructions are now in 32-bit
format. As a result, some of them are encoded differently. Some
instructions may take up more bytes in their binary form, some others
maybe less, and other still remain unchanged. At any rate, we can't
continue executing any more code just yet, because of the prefetch
queue.
You see, CPUs are built to be fast. One of the tricks of the trade that
make CPUs ever faster is to have the CPU load a range of instructions
from memory to be executed at the same time, rather than just one. This
is called prefetching. After all, the CPU in the Intel 80386 processor
can read 4 bytes (32 bits) at the same time from memory, and that might
well be more than one instruction. For technical reasons, even more
might be read and decoded before it's actually executed by the CPU.
The consequence of this is that the CPU may have read some instructions
from memory when it was still in 16-bits mode, decoded them, and is now
ready to execute them. They won't work, because the processor is now in
protected 32-bits mode!
Luckily, there is trick to make the processor discard the instructions
it has already prefetched, and that trick is jumping. Whenever the
processor encounters a jump instruction, any instructions it had read
past that instructions become worthless and must be discarded.
Consequently, jumping clears the prefetch queue:
.macro mClearPrefetchQueue
jmp clear_prefetch_queue:
nop
nop
clear_prefetch_queue:
.endm
There are some nop instructions after the jump, to make doubly sure
that the prefetch queue is fully emptied.
Setting up the 80386's registers
We've talked the talk, now the time has come to walk the walk. Next,
we'll set up the memory segments that our future kernel code will use.
This is no longer done by putting in memory addresses, but by
specifying selector numbers. We'll set all our data segments (ds, es,
fs and gs) as well as the stack segment (ss) to use selector 2 from the
global descriptor table, which corresponds to the data segment that we
had defined in our GDT:
.macro mSetup386Segments
mov ax, 0x10 # Byte offset for selector 2
mov ds, ax # (remember, each descriptor is 8 bytes)
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x2ffff # Set stack to grown downwards from 0x30000
.endm
Jumping to the kernel
Yes! Assuming that our second-stage boot loader had previously loaded
our kernel image into memory at linear address 0x20000 (using the same
FAT reading at file reading routines we had already developed for the
first-stage bootloader), we can now jump to it and start executing it.
The jump to the kernel must be done with a 32-bit long jump
instruction. Here we face a small snag. All the code in our
second-stage boot loader is 16-bit code, because that's the way it's
compiled. Therefore, we cannot actually specify a 32-bit long jump; it
will get compiled as a 16-bit jump. To get around this, we'll encode
the long jump instruction ourselves just like a 32-bit assembler would
do it.
Our long jump instruction will jump to linear memory address 0x20000,
in the first selector of the GDT (our code segment), which has offset
0x8:
.macro mJumpToKernel
.byte 0x66
.byte 0xEA
.int 0x20000 # offset
.word 0x0008 # selector word
.endm
This will transfer control to the kernel code, which we have yet to
write. If you're feeling adventurous, why not write a small 32-bit
assembly program that places the value 0x41 at linear address 0xb8000?
That will show the letter "A" at the top-left corner of the screen and
can be executed in protected mode (you can't use the BIOS interrupts to
write to the screen anymore).
Actually, we'll do that in the next part of this tutorial anyway!
Summary
Whew! It's been quite a trip, but we have now reached protected mode
and are ready to write a simple kernel. At least at this point, all of
the machine's memory and protected mode features will be at our
disposal.
In this tutorial, we've wrapped up the final bits necessary to enter
protected mode:
* We've enabled the PE-bit in the CR0 register, thus switching to
protected mode
* We've cleared the prefetch queue so that no 16-bit instructions
remained in the CPU which can no longer be executed
* We've setup the registers for use by the 32-bit kernel program
* We've executed a long jump to the kernel code
[14]Continue on to the next part of this guide!
[INS: :INS]
Please enable JavaScript to view the [15]comments powered by Disqus.
[independent-software-logo.svg]
* 1513 Av. Vladimir Lenine
* Maputo, Mozambique
* [16]info@independent-software.com
* +258 82 304 26 35
Links
* [17]Web Development
* [18]Portfolio
* [19]Operating System Development
Social
* [20]Facebook
* [21]Github
* [22]LinkedIn
* [23]Twitter
* [24]Google+
* [25]Atom feed
References
Visible links:
1. http://www.independent-software.com/feed.xml
2. http://www.independent-software.com/
3. http://www.independent-software.com/about-independent-software.html
4. http://www.independent-software.com/philosophy.html
5. http://www.independent-software.com/web-development.html
6. http://www.independent-software.com/portfolio.html
7. http://www.independent-software.com/blog.html
8. http://www.independent-software.com/category/osdev.html
9. http://www.independent-software.com/category/asm.html
10. http://www.independent-software.com/{{%20site.baseurl%20}}{%%20post_url%20/osdev/2013-10-23-operating-system-development-enabling-a20-line%20%}
11. http://www.independent-software.com/operating-system-development.html
12. http://www.independent-software.com/operating-system-development-enabling-a20-line.html
13. http://www.independent-software.com/operating-system-development.html
14. http://www.independent-software.com/operating-system-development-first-and-second-stage-bootloaders.html
15. https://disqus.com/?ref_noscript
16. mailto:info@independent-software.com
17. http://www.independent-software.com/web-development.html
18. http://www.independent-software.com/portfolio.html
19. http://www.independent-software.com/operating-system-development.html
20. https://www.facebook.com/Independent-Software-295360497495620/
21. https://github.com/henck
22. https://www.linkedin.com/company/independent-software-mozambique-
23. https://twitter.com/IndependentSw
24. https://google.com/+Independent-software
25. http://www.independent-software.com/feed.xml
Hidden links:
27. http://www.independent-software.com/operating-system-development-jumping-to-protected-mode.html
|