# linux/boot/head.s
# (C) 1991
Linus Torvalds
# head.s contains the 32-bit startup
code.
# NOTE!!! Startup happens at absolute address 0x00000000,
which is also where
# the page directory will exist. The startup code will be
overwritten by
#the page directory.
.text
.globl _idt,_gdt,_pg_dir,_tmp_floppy_area
_pg_dir:
startup_32:
movl $0x10,% eax
mov % ax,% ds
mov % ax,% es
mov % ax,% fs
mov % ax,% gs
lss _stack_start,% esp #_stack_start-->ss:esp 设置系统堆栈
call setup_idt #将中断描述符表idt设置成具有256个项,并都指向ignore
int中断门,然后
#加载中断描述符表寄存器(用lidt指令)
call setup_gdt #设置全局描述符表寄存器
movl $0x10,% eax # reload all the segment registers
mov % ax,% ds # after changing gdt. CS was already
mov % ax,% es # reloaded in 'setup_gdt'
mov % ax,% fs
mov % ax,% gs
lss _stack_start,% esp
xorl % eax,% eax
1: incl % eax # check that A20 really IS enabled
movl % eax,0x000000 # loop forever if it isn't
cmpl % eax,0x100000
je 1b
movl % cr0,% eax # check math chip
andl $0x80000011,% eax # Save PG,PE,ET
orl $2,% eax # set MP
movl % eax,% cr0
call check_x87
jmp after_page_tables
#finit
向协处理器发出初始化命令,它会把协处理器置于一个未受以前操作影响的已知状态,设置其控制字为默认值、清除状态字和所有浮点栈式寄存器。非等待形式的这条指令(fninit)还会让协处理器终止执行当前正在执行的任何先前的算术操作。fstsw
指令取协处理器的状态字。如果系统中存在协处理器的话,那么在执行了fninit指令后其状态字低字节肯定为0。
check_x87:
fninit
fstsw % ax
cmpb $0,% al
je 1f #check 是否含有协处理器
movl % cr0,% eax
xorl $6,% eax #reset MP EM 位
movl % eax,% cr0
ret
.align 2
1: .byte 0xDB,0xE4 # fsetpm for 287, ignored
by 387
ret
setup_idt:
lea ignore_int,% edx
movl $0x00080000,% eax
movw % dx,% ax
movw $0x8E00,% dx
lea _idt,% edi
mov $256,% ecx
rp_sidt:
movl % eax,(% edi)
movl % edx,4(% edi)
addl $8,% edi
dec % ecx
jne rp_sidt
lidt idt_descr
ret
setup_gdt:
lgdt gdt_descr
ret
.org 0x1000
pg0:
.org 0x2000
pg1:
.org 0x3000
pg2:
.org 0x4000
pg3:
.org 0x5000
_tmp_floppy_area:
.fill 1024,1,0
after_page_tables:
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
pushl $L6 # return address for main, if it decides to.
pushl $_main
jmp setup_paging
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.
int_msg:
.asciz "Unknown interrupt\n\r"
.align 2
ignore_int:
pushl % eax
pushl % ecx
pushl % edx
push % ds
push % es
push % fs
movl $0x10,% eax
mov % ax,% ds
mov % ax,% es
mov % ax,% fs
pushl $int_msg
call _printk
popl % eax
pop % fs
pop % es
pop % ds
popl % edx
popl % ecx
popl % eax
iret
#将
.align 2
setup_paging:
movl $1024*5,% ecx #5 pages : pg_dir+4 page
tables
xorl % eax,% eax # pg_dir is at
0x000
xorl % edi,% edi
cld;rep;stosl #把eax中的内容存放到es:edi指定内存中,edi+4, 清零前5页内存
movl $ pg0+7,_pg_dir #set present bit/user r/w
在第一个页表中
movl pg1+7,_pg_dir+4 #set
present bit/user r/w 在第二个页表中
movl $pg2+7,_pg_dir+8
movl $pg3+7,_pg_dir+12
movl $pg3+4092,% edi #es:edi -->指向最后一页的最后一项
movl $0xfff007,% eax
#最后一个页表的最后一项里面指定的物理内存页面地址是0xfff000,7是属性
std #方向位值位,edi-4
1: stosl # fill pages backwards - more
efficient :-)
subl $0x1000,% eax # 每设置好一项,物理地址减0x1000(4K)
jge 1b
xorl % eax,% eax # pg_dir is at
0x0000
movl % eax,% cr3 # cr3 : page directory
start
movl % cr0,% eax
orl $0x80000000,% eax
movl % eax,% cr0 #set paging (PG)
bit
ret #this also flushes
prefetch-queue ,改变分页标志后需要更新。跳转到main
#执行,出栈到/init/main.c中运行
.align 2
.word 0
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long _idt
.align 2
.word 0
gdt_descr:
.word 256*8-1 # so does gdt (not that that's any
.long _gdt # magic number, but it works for me :^)
.align 3
_idt: .fill 256,8,0 # idt is uninitialized
_gdt: .quad 0x0000000000000000
.quad 0x00c09a0000000fff
.quad 0x00c0920000000fff
.quad 0x0000000000000000
.fill 252,8,0