初始化临时页表的工作由startup_32函数完成,本文要讲的startup_32函数定义在arch/i386/kernel/head.S文件中。其代码如下(解析请见注释)。
1
ENTRY(startup_32)
2
3
/*
4
* Set segments to known values.
5
*/
6
cld
7
lgdt boot_gdt_descr
-
__PAGE_OFFSET
8
movl $(__BOOT_DS),
%
eax
9
movl
%
eax,
%
ds
10
movl
%
eax,
%
es
11
movl
%
eax,
%
fs
12
movl
%
eax,
%
gs
13
14
/*
15
* Clear BSS first so that there are no surprises...
16
* No need to cld as DF is already clear from cld above...
17
*/
18
xorl
%
eax,
%
eax
19
movl $__bss_start
-
__PAGE_OFFSET,
%
edi
20
movl $__bss_stop
-
__PAGE_OFFSET,
%
ecx
21
subl
%
edi,
%
ecx
22
shrl $
2
,
%
ecx
23
rep ; stosl
24
25
/*
26
* Initialize page tables. This creates a PDE and a set of page
27
* tables, which are located immediately beyond _end. The variable
28
* init_pg_tables_end is set up to point to the first "safe" location.
29
* Mappings are created both at virtual address 0 (identity mapping)
30
* and PAGE_OFFSET for up to _end+sizeof(page tables)+INIT_MAP_BEYOND_END.
31
*
32
* Warning: don't use %esi or the stack in this code. However, %esp
33
* can be used as a GPR if you really need it...
34
*/
35
page_pde_offset
=
(__PAGE_OFFSET
>>
20
);
/*
__PAGE_OFFSET是0xc0000000,page_pde_offset = 3072 = 0xc00,是页目录中的偏移
*/
36
37
movl $(pg0
-
__PAGE_OFFSET),
%
edi
/*
将pg0对应的物理地址送到edi
*/
38
movl $(swapper_pg_dir
-
__PAGE_OFFSET),
%
edx
/*
将swapper_pg_dir(存放临时页全局目录的地址)送到edx
*/
39
movl $
0x007
,
%
eax
/*
0x007 = PRESENT+RW+USER
*/
40
10
:
41
leal
0x007
(
%
edi),
%
ecx
/*
Create PDE entry —— 构造一个页目录项(地址+标志位),把edi指向的物理地址加上0x007放入ecx中
*/
42
movl
%
ecx,(
%
edx)
/*
第一次循环时把ecx中的内容放入swapper_pg_dir的第0项里,第二次循环时把ecx中的内容放入swapper_pg_dir的第1项里
*/
43
movl
%
ecx,page_pde_offset(
%
edx)
/*
Store kernel PDE entry
*/
44
addl $
4
,
%
edx
/*
edx加上一个页目录项长度(4字节),指向页全局目录的下一个页目录项地址
*/
45
movl $
1024
,
%
ecx
/*
初始化1024个页目录项,设置计数器
*/
46
11
:
47
stosl
/*
eax -> [edi]; edi = edi + 4
*/
48
addl $
0x1000
,
%
eax
/*
更改eax的值,为下次stosl作准备
*/
49
loop 11b
/*
循环操作,其实就是初始化1024个页表项
*/
50
/*
End condition: we must map up to and including INIT_MAP_BEYOND_END
*/
51
/*
bytes beyond the end of our own page tables; the +0x007 is the attribute bits
*/
52
leal (INIT_MAP_BEYOND_END
+
0x007
)(
%
edi),
%
ebp
/*
ebp = edi指向的物理地址 + INIT_MAP_BEYOND_END(128K)+ 0x007
*/
53
cmpl
%
ebp,
%
eax
/*
比较ebp与eax
*/
54
jb 10b
/*
如果eax < ebp,则跳到上面10的地方
*/
55
movl
%
edi,(init_pg_tables_end
-
__PAGE_OFFSET)
/*
此时的edi中存放pg0+0x2000,将此值存入init_pg_tables_end中,表示页表初始化结束
*/
56
57
#ifdef CONFIG_SMP
58
xorl
%
ebx,
%
ebx
/*
This is the boot CPU (BSP)
*/
59
jmp 3f
60
61
/*
62
* Non-boot CPU entry point; entered from trampoline.S
63
* We can't lgdt here, because lgdt itself uses a data segment, but
64
* we know the trampoline has already loaded the boot_gdt_table GDT
65
* for us.
66
*/
67
ENTRY(startup_32_smp)
68
cld
69
movl $(__BOOT_DS),
%
eax
70
movl
%
eax,
%
ds
71
movl
%
eax,
%
es
72
movl
%
eax,
%
fs
73
movl
%
eax,
%
gs
74
75
/*
76
* New page tables may be in 4Mbyte page mode and may
77
* be using the global pages.
78
*
79
* NOTE! If we are on a 486 we may have no cr4 at all!
80
* So we do not try to touch it unless we really have
81
* some bits in it to set. This won't work if the BSP
82
* implements cr4 but this AP does not -- very unlikely
83
* but be warned! The same applies to the pse feature
84
* if not equally supported. --macro
85
*
86
* NOTE! We have to correct for the fact that we're
87
* not yet offset PAGE_OFFSET..
88
*/
89
#define
cr4_bits mmu_cr4_features-__PAGE_OFFSET
90
movl cr4_bits,
%
edx
91
andl
%
edx,
%
edx
92
jz 6f
93
movl
%
cr4,
%
eax # Turn on paging options (PSE,PAE,..)
94
orl
%
edx,
%
eax
95
movl
%
eax,
%
cr4
96
97
btl $
5
,
%
eax # check
if
PAE
is
enabled
98
jnc 6f
99
100
/*
Check if extended functions are implemented
*/
101
movl $
0x80000000
,
%
eax
102
cpuid
103
cmpl $
0x80000000
,
%
eax
104
jbe 6f
105
mov $
0x80000001
,
%
eax
106
cpuid
107
/*
Execute Disable bit supported?
*/
108
btl $
20
,
%
edx
109
jnc 6f
110
111
/*
Setup EFER (Extended Feature Enable Register)
*/
112
movl $
0xc0000080
,
%
ecx
113
rdmsr
114
115
btsl $
11
,
%
eax
116
/*
Make changes effective
*/
117
wrmsr
118
119
6
:
120
/*
This is a secondary processor (AP)
*/
121
xorl
%
ebx,
%
ebx
122
incl
%
ebx
123
124
3
:
125
#endif
/* CONFIG_SMP */
126
127
/*
128
* Enable paging
129
*/
130
movl $swapper_pg_dir
-
__PAGE_OFFSET,
%
eax
131
movl
%
eax,
%
cr3
/*
set the page table pointer.. ——将页全局目录的地址送入cr3寄存器中
*/
132
movl
%
cr0,
%
eax
133
orl $
0x80000000
,
%
eax
/*
设置PG位
*/
134
movl
%
eax,
%
cr0
/*
..and set paging (PG) bit
*/
135
ljmp $__BOOT_CS,$1f
/*
Clear prefetch and normalize %eip
*/
136
1
:
137
/*
Set up the stack pointer
*/
138
lss stack_start,
%
esp
/*
建立内核堆栈
*/
139
140
/*
141
* Initialize eflags. Some BIOS's leave bits like NT set. This would
142
* confuse the debugger if this code is traced.
143
* XXX - best to initialize before switching to protected mode.
144
*/
145
/*
将eflags寄存器清零
*/
146
pushl $
0
147
popfl
148
149
#ifdef CONFIG_SMP
150
andl
%
ebx,
%
ebx
151
jz 1f
/*
Initial CPU cleans BSS
*/
152
jmp checkCPUtype
153
1
:
154
#endif
/* CONFIG_SMP */
155
156
/*
157
* start system 32-bit setup. We need to re-do some of the things done
158
* in 16-bit mode for the "real" operations.
159
*/
160
call setup_idt
/*
设置中断描述附表
*/
161
162
/*
163
* Copy bootup parameters out of the way.
164
* Note: %esi still has the pointer to the real-mode data.
165
*/
166
movl $boot_params,
%
edi
167
movl $(PARAM_SIZE
/
4
),
%
ecx
168
cld
169
rep
170
movsl
171
movl boot_params
+
NEW_CL_POINTER,
%
esi
172
andl
%
esi,
%
esi
173
jnz 2f # New command line protocol
174
cmpw $(OLD_CL_MAGIC),OLD_CL_MAGIC_ADDR
175
jne 1f
176
movzwl OLD_CL_OFFSET,
%
esi
177
addl $(OLD_CL_BASE_ADDR),
%
esi
178
2
:
179
movl $saved_command_line,
%
edi
180
movl $(COMMAND_LINE_SIZE
/
4
),
%
ecx
181
rep
182
movsl
183
1
:
184
checkCPUtype:
185
186
movl $
-
1
,X86_CPUID #
-
1
for
no CPUID initially
187
188
/*
check if it is 486 or 386.
*/
189
/*
190
* XXX - this does a lot of unnecessary setup. Alignment checks don't
191
* apply at our cpl of 0 and the stack ought to be aligned already, and
192
* we don't need to preserve eflags.
193
*/
194
195
movb $
3
,X86 # at least
386
196
pushfl # push EFLAGS
197
popl
%
eax #
get
EFLAGS
198
movl
%
eax,
%
ecx # save original EFLAGS
199
xorl $
0x240000
,
%
eax # flip AC and ID bits
in
EFLAGS
200
pushl
%
eax # copy to EFLAGS
201
popfl #
set
EFLAGS
202
pushfl #
get
new
EFLAGS
203
popl
%
eax # put it
in
eax
204
xorl
%
ecx,
%
eax # change
in
flags
205
pushl
%
ecx # restore original EFLAGS
206
popfl
207
testl $
0x40000
,
%
eax # check
if
AC bit changed
208
je is386
209
210
movb $
4
,X86 # at least
486
211
testl $
0x200000
,
%
eax # check
if
ID bit changed
212
je is486
213
214
/*
get vendor info
*/
215
xorl
%
eax,
%
eax # call CPUID with
0
->
return
vendor ID
216
cpuid
217
movl
%
eax,X86_CPUID # save CPUID level
218
movl
%
ebx,X86_VENDOR_ID # lo
4
chars
219
movl
%
edx,X86_VENDOR_ID
+
4
# next
4
chars
220
movl
%
ecx,X86_VENDOR_ID
+
8
# last
4
chars
221
222
orl
%
eax,
%
eax #
do
we have processor info
as
well
?
223
je is486
224
225
movl $
1
,
%
eax # Use the CPUID instruction to
get
CPU type
226
cpuid
227
movb
%
al,
%
cl # save reg
for
future use
228
andb $
0x0f
,
%
ah # mask processor family
229
movb
%
ah,X86
230
andb $
0xf0
,
%
al # mask model
231
shrb $
4
,
%
al
232
movb
%
al,X86_MODEL
233
andb $
0x0f
,
%
cl # mask mask revision
234
movb
%
cl,X86_MASK
235
movl
%
edx,X86_CAPABILITY
236
237
is486: movl $
0x50022
,
%
ecx #
set
AM, WP, NE and MP
238
jmp 2f
239
240
is386: movl $
2
,
%
ecx #
set
MP
241
2
: movl
%
cr0,
%
eax
242
andl $
0x80000011
,
%
eax # Save PG,PE,ET
243
orl
%
ecx,
%
eax
244
movl
%
eax,
%
cr0
245
246
call check_x87
247
incb ready
248
lgdt cpu_gdt_descr
249
lidt idt_descr
250
ljmp $(__KERNEL_CS),$1f
251
1
: movl $(__KERNEL_DS),
%
eax # reload all the segment registers
252
movl
%
eax,
%
ss # after changing gdt.
253
254
movl $(__USER_DS),
%
eax # DS
/
ES contains
default
USER segment
255
movl
%
eax,
%
ds
256
movl
%
eax,
%
es
257
258
xorl
%
eax,
%
eax # Clear FS
/
GS and LDT
259
movl
%
eax,
%
fs
260
movl
%
eax,
%
gs
261
lldt
%
ax
262
cld # gcc2 wants the direction flag cleared at all times
263
#ifdef CONFIG_SMP
264
movb ready,
%
cl
265
cmpb $
1
,
%
cl
266
je 1f # the first CPU calls start_kernel
267
# all other CPUs call initialize_secondary
268
call initialize_secondary
269
jmp L6
270
1
:
271
#endif
/* CONFIG_SMP */
272
call start_kernel
/*
调用start_kernel函数
*/
273
L6:
274
jmp L6 # main should never
return
here, but
275
# just
in
case
, we know what happens.