linux0.12-6-4(head.s)-程序员宅基地

技术标签: linux0.12  运维  linux  服务器  

[259页]

6-4 head.s程序

6-4-1 功能描述

(a)首先是加载各个数据段寄存器。
(b)重新设置中断描述符idt,共256项,并使各个表项均指向一个只报错误的哑中断子程序ignore_int。
中断门描述符中段选择符设置为0x0008,表示该哑中断处理子程序在内核中。
本程序又重新设置了全局段描述符表gdt。
(d)检查A20地址线是否已真的开启。
(e)设置管理内存的分页处理机制。
(f)head.s程序利用返回指令将预先放置在堆栈中的/init/main.c程序的入口地址弹出,去运行main()程序。

6-4-2 代码注释

/*
 *  linux/boot/head.s
 *
 *  (C) 1991  Linus Torvalds
 */

/*
head.s含有32位启动代码。
注意!!!!32位启动代码是从绝对地址0x00开始的,这里也同样是页目录将存在的地方,
因此这里的启动代码将被页目录覆盖掉。
*/
.text
.globl _idt,_gdt,_pg_dir,_tmp_floppy_area
_pg_dir:			#页目录将会存放在这里。

/*
$0x10的含义是请求特权级0(位0-1=0)、选择全局描述符(位2=0)、
选择表中第2项(为3-15=2)。
*/
startup_32:
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs
	lss _stack_start,%esp		#将堆栈放置在stack_start指向的user_stack数组区。
	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					#因为修改了gdt,所以需要重新装载所有的段寄存器。
	mov %ax,%gs					#CS代码段寄存器已经在setup_gdt中重新加载过了。
/*作者的意思:有点问题,但没有产生问题的原因:段限长没有超过8MB,且后面内核执行过程中会重新加载CS*/
	lss _stack_start,%esp
/*
测试A20地址线是否已经开启。在0地址写入值,检查0x100000(1MB)处是否一致,如果是一致检查,否则跳出。
*/
	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
/*
注意!在下面这段程序中,486应该将位16置位,以检查在超级用户模式下的写保护,此后"verify_area()"
调用就不需要了。486的用户通常也会想将NE(#5)置位,以便对数学协处理器的出错使用int 16。
*/
#上面原注释中提到的486CPU中CR0控制寄存器的位16是写保护标志WP,
#用于禁止超级用户级的程序向一般用户只读页面中进行写操作。该标志主要用于操作系统
#在创建新进程时实现写时复制方法。

/*
下面这段程序用于检查数学协处理器芯片是否存在。方法是修改控制寄存器CR0,在假设
存在协处理器的情况下执行一个协处理器指令,如果出错的话则说明协处理器芯片不存在,需要设置
CR0中的协处理器仿真位EM(bit2),并复位协处理器存在标志MP(bit1)
*/
	movl %cr0,%eax		# check math chip
	andl $0x80000011,%eax	# Save PG,PE,ET
/* "orl $0x10020,%eax" here for 486 might be good */
	orl $2,%eax		# set MP
	movl %eax,%cr0
	call check_x87
	jmp after_page_tables

/*
 * 我们依赖于ET标志的正确性来检测287/387存在与否。
 */
check_x87:
	fninit				#向协处理器发出初始化命令。
	fstsw %ax			#取协处理器状态字到ax寄存器中。
	cmpb $0,%al			#初始化后状态字应该为0,否则说明协处理器不存在。
	je 1f				#如果存在则向前跳转到标号1处,否则修改cr0.
	movl %cr0,%eax		
	xorl $6,%eax		/* reset MP, set EM */
	movl %eax,%cr0
	ret
/*
下面的两个字节值是80287协处理器指令fsetpm的机器码。其作业是把80287设置为保护模式。
80387无需该指令,并且将会把该指令看作是空操作。
*/	
.align 2
1:	.byte 0xDB,0xE4		/* fsetpm for 287, ignored by 387 */ #287协处理器码
	ret

/*
下面这段是设置中断描述符表子程序 setup_idt
将中断描述符表idt设置成具有256个项,并都指向ignore_int中断门。然后加载中断描述符表
寄存器(用lidt指令)。真正实用的中断门以后再安装。当我们再其他地方认为一切都正常时再开启中断。
该子程序将会被页表覆盖掉。
*/
#中断描述符表中的项虽然也是8字节组成,但其格式与全局表中的不同,被称为门描述符(Gate Descriptor)。
#它的0-1,6-7字节是偏移量,2-3字节是选择符,4-5字节是一些标志。
#这段代码首先在edx、eax中组合设置处8字节默认的中断描述符值,然后再idt表每一项中都放置
#该描述符,共256项。eax含有描述符低4字节,edx含有高4字节。内核在随后的初始化过程中会
#替换安装那些真正实用的中断描述符项。
setup_idt:
	lea ignore_int,%edx				#将ignore_int的有效地址(偏移值)值->edx寄存器
	movl $0x00080000,%eax			#将选择符0x0008置入eax的高16位中。
	movw %dx,%ax					/* selector = 0x0008 = cs */
									#偏移值的低16位置入eax的低16位中。此时eax含有门描述符低4字节的值。
	movw $0x8E00,%dx				/* interrupt gate - dpl=0, present */ #此时edx含有门描述符高4字节的值。
	lea _idt,%edi					#_idt是中断描述符表的地址。
	mov $256,%ecx
rp_sidt:
	movl %eax,(%edi)				#将哑中断门描述符存入表中。
	movl %edx,4(%edi)				#eax内容放到edi+4所指内存位置处。
	addl $8,%edi					#edi指向表中下一项。
	dec %ecx
	jne rp_sidt
	lidt idt_descr					#加载中断描述符表寄存器值。
	ret

/*
设置全局描述符表项setup_gdt
这个子程序设置一个新的全局描述符gdt,并加载。此时仅创建了两个表项,
与前面的一样。该子程序只有两行,"非常的"复杂,所以当然需要这么长的注释了。
该子程序将被页表覆盖掉。
*/
setup_gdt:
	lgdt gdt_descr			#加载全局描述符表寄存器。
	ret

/*
Linus将内核的内存页表直接放在页目录之后,使用了4个表来寻址16MB的物理内存。
如果你有多余16MB的内存,就需要在这里进行扩展修改。
*/
#每个页表长为4KB字节(1页内存页面),而每个页表项需要4个字节,因此一个页表共可以存放
#1024个表项。如果一个页表项寻址4KB的地址空间,则一个页表就可以寻址4MB的物理内存。
#页表项的格式为:项的前0~11位存放一些标志,例如是否在内存中(P位0)、读写许可(R/W位1)、
#普通用户还是超级用户使用(U/S位2)、是否修改过(是否脏了)(D位6)等;表项的位12~31是
#页框地址,用于指出一页内存的物理起始地址。
.org 0x1000			#从偏移0x1000处开始时第1个页表(偏移0开始处将存放页表目录)。
pg0:

.org 0x2000
pg1:

.org 0x3000
pg2:

.org 0x4000
pg3:

.org 0x5000				#定义下面的内存数据块从偏移0x5000处开始。
/*
当DMA(直接存储器访问)不能访问缓冲块时,下面的tmp_floppy_area内存块
就可供软盘驱动程序使用。其地址需要对其调整,这样就不会跨越64KB边界。
*/
_tmp_floppy_area:
	.fill 1024,1,0		#工保留1024项,每项1B,填充数值0/*
下面这几个入栈操作用于跳转到init/main.c中的main()函数准备工作。第139行上的指令
在栈中压入了返回地址,而第140行则压入了main()函数代码地址。当head.s最后再第218行
执行ret指令时就会弹出main()的地址,并把控制权转移到init/main.c程序中。参见第3章中
有关C函数调用机制的说明。
*/
#前面3个入栈0值应该分别表示envp、argv指针和 argc的值,但main()没有用到。
#
after_page_tables:
	pushl $0		# These are the parameters to main :-)
	pushl $0
	pushl $0
	pushl $L6		# main函数退出时,返回到L6,方便分析问题。
	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								#按4字节方式对齐内存地址。
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				#中断返回


/*
上面英文注释第2段的含义是指在机器物理内存中大于1MB的内存空间主要被用于主内存区。主内存
区空间由mm模块管理。它涉及页面映射操作。内核中所有其它函数就是这里指的一般(普通)函数。
若要使用主内存区的页面,就需要使用get_free_page()等函数获取。因为主内存区中内存页面是
共享资源的,必须又程序进行统一管理以避免资源争用和竞争。

在内存物理地址0x0处开始存放1页页目录表和4页页表。页目录表是系统所有进程公用的,而这里
的4页页表则属于内核专用,它们一一映射线性地址起始16MB空间范围到物理内存上。对于新的进程,
系统会在主内存区为其申请页面存放页表。另外,1页内存长度是4096字节。
 */
.align 2					#按4字节方式对齐内存地址边界。
setup_paging:				#首先对5页内存(1页目录+4页页表)清0。
	movl $1024*5,%ecx		/* 5 pages - pg_dir+4 page tables */
	xorl %eax,%eax
	xorl %edi,%edi			/* pg_dir is at 0x000 */
							#页目录从0x000地址开始。
	cld;rep;stosl			#eax内容存到es:edi所指内存位置处,且edi增4。
	
	
	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		/*  --------- " " --------- */
/*
将4个页表都填入0xfff007
*/	
	movl $pg3+4092,%edi		#edi->最后一页的最后一项。
	movl $0xfff007,%eax		/*  16Mb - 4096 + 7 (r/w user,p) */
	std						#方向位置位,edi值递减4字节。
1:	stosl					/* fill pages backwards - more efficient :-) */
	subl $0x1000,%eax		#每填写号一项,物理地址值减0x1000.
	jge 1b					#如果小于0则说明全填写号了。
#设置页目录表基地址寄存器CR3的值,指向页目录表。CR3中保存的是页目录表的物理地址。	
	xorl %eax,%eax		/* pg_dir is at 0x0000 页目录表在0x0000处。*/
	movl %eax,%cr3		/* cr3 - page directory start */
#设置启动使用分页处理(cr0的PG标志,位31)	
	movl %cr0,%eax
	orl $0x80000000,%eax	#添上PG标志
	movl %eax,%cr0		/* set paging (PG) bit */
	ret			/* this also flushes prefetch-queue */
/*
在修改分页处理标志后要求使用转移指令刷新预取指令队列,这里用的是返回指令ret。
该运行指令的另一个作业是将140行压入堆栈中的main程序地址弹出,并跳转到/init/main.c 
程序去运行。本程序到此就真正结束了。
*/


=====================================================
#下面是加载中断描述符表寄存器idtr的指令lidt要求的6字节操作数。前2字节是idt表的限长,
#后4字节是idt表在线性地址空间中的32位基地址。
.align 2
.word 0
idt_descr:
	.word 256*8-1		# idt contains 256 entries
	.long _idt
	
#下面加载全局描述符表寄存器gdtr的指令lgdt要求的6字节操作数。前2字节是gdt表的限长,
#后4字节是gdt表的线性基地址。这里全局长度设置为2KB字节(0x7ff即可),因为每8字节
#组成一个描述符项,所以表中共可有256项。符号_gdt是全局表在本程序中的偏移位置,见第234
.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

#全局表。前4想分别是空项、内核代码段描述符、内核数据段描述符、
#系统调用段描述符(但实际没有用)、后面还预留252想的空间,
#用于防止创建任务的局部描述符LDT和对应的任务状态段TSS的描述符。
_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 */

6-4-3 其他信息

1、 程序执行结束后的内存映像
内核模块在内存中的 详细映像如图6-11所示。

2、 Intel32位保护运行机制
在保护模式下,段寄存器中存放的是一个描述符在描述符表中的偏移地址值。

3、 前导符(伪指令)align

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_31806069/article/details/130388384

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法