Linux0.11内核源代码(3)

	movl $0x10,%eax/*设置数据寄存器,注意CS段寄存器设置为8,而数据寄存器设置为16.仅一位有差距*/
	mov %ax,%ds
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs
	lss _stack_start,%esp
	call setup_idt
	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
check_x87:
和之前一样,代码起始位置设置段寄存器,由于已经定义了GDTR;所以这里只需要设置相应的索引就可以了。注意这里设置的索引刚好是GDTR中的第二项。由于第二项的基地址是0x0,因此以后要读取相应的数据就需要加上偏移,(数据都存放在0x8000以上)。然后开始设置堆栈段和堆栈指针。lss就可以实现。其中stack_start的定义如下:
struct {
	long * a;
	short b;
	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
其中,堆栈段寄存器同样被设置为0x10,而堆栈指针则是一个在编译时推导出来的值。现在只要知道这个位置设置很安全就可以了,相关的数据会在下一步分析。设置堆栈之后就可以重新设置IDTR和GDTR了,由于在之前的IDTR中并没有实质性的数据。由于GDT变化了,所以相应的需要重新设置段寄存器和堆栈指针。然后验证内存是否回卷,如果回卷,则写到0x000000处的数据将会出现在0x100000处。到了这一步,因为原来有设置CR0寄存器,所以需要将原来的数值保存起来。
check_x87:
	fninit
	fstsw %ax
	cmpb $0,%al
	je 1f			/* no coprocessor: have to set bits */
	movl %cr0,%eax
	xorl $6,%eax		/* reset MP, set EM */
	movl %eax,%cr0
	ret
.align 2
1:	.byte 0xDB,0xE4		/* fsetpm for 287, ignored by 387 */
	ret
然后测试是否存在浮点数处理器,如果没有浮点数处理器则需要设置CR0中的相应的位。
setup_idt:
	lea ignore_int,%edx
	movl $0x00080000,%eax
	movw %dx,%ax		/* selector = 0x0008 = cs */
	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
	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

对IDTR的初始化,也是一个比较复杂的位处理,不过和GDTR表项的设置一样,整个表项比较重要的是段选择器(段基址),然后相对段的偏移以及DPL。整个IDT表项如同上面一样,其中低32位存放在EAX中,而高32位则存放在EDX中。然后,利用循环开始填充IDT表项,最后将IDT表项给存放到IDTR中。注意IDTR中的基址和上限的设置。同样对GDTR的设置也很简单。

.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
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
after_page_tables中开始,首先进行压栈操作,然后将主函数的地址也存放到堆栈中,因此调用主函数只需要一个ret返回就可以了。

下面的ignore_int函数,首先保存寄存器。然后重新设置段寄存器,这里的设置使得整个代码在内核中进行处理,注意此时代码段已经设置好了。设置之后调用内核的printk函数进行打印输出。

.align 2
setup_paging:
	movl $1024*5,%ecx		/* 5 pages - pg_dir+4 page tables */
	xorl %eax,%eax
	xorl %edi,%edi			/* pg_dir is at 0x000 */
	cld;rep;stosl
	movl $pg0+7,_pg_dir		/* set present bit/user r/w */
	movl $pg1+7,_pg_dir+4		/*  --------- " " --------- */
	movl $pg2+7,_pg_dir+8		/*  --------- " " --------- */
	movl $pg3+7,_pg_dir+12		/*  --------- " " --------- */
	movl $pg3+4092,%edi
	movl $0xfff007,%eax		/*  16Mb - 4096 + 7 (r/w user,p) */
	std
1:	stosl			/* fill pages backwards - more efficient :-) */
	subl $0x1000,%eax
	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 */
.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	/* NULL descriptor */
	.quad 0x00c09a0000000fff	/* 16Mb */
	.quad 0x00c0920000000fff	/* 16Mb */
	.quad 0x0000000000000000	/* TEMPORARY - don't use */
	.fill 252,8,0			/* space for LDT's and TSS's etc */
Linux对页面的处理分为两个部分,第一个是页目录表,第二项才是页表,最后才是偏移。16MB内存下,有4K个页,则需要4K*4个页表项建立地址映射关系,这四个页表存在于页目录表中,在这里存储在内存地址为0的页中,因此需要清除5个页表为0,然后进行设置。在设置页目录后就对页表进行设置,页表中每四个字节存储一个物理地址,这个物理地址和虚拟地址进行映射找到物理地址。不过这里的映射关系都是直接对应的映射关系。也就是高位内存存放在物理的高位内存中。因此在接下来的访问中和没有设置页表是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值