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

智能推荐

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

深度神经网络在训练初期的“梯度消失”或“梯度爆炸”的问题解决:数据标准化(Data Standardization),权重初始化(Weight Initialization),Dropout正则化等_在人工神经网络研究的初始阶段,辛顿针对训练过程中常出现的梯度消失现象, 提供相-程序员宅基地

文章浏览阅读101次。1986年,深度学习(Deep Learning)火爆,它提出了一个名为“深层神经网络”(Deep Neural Networks)的新型机器学习模型。随后几年,神经网络在图像、文本等领域取得了惊艳成果。但是,随着深度学习的应用范围越来越广泛,神经网络在遇到新的任务时出现性能下降或退化的问题。这主要是由于深度神经网络在训练初期面临着“梯度消失”或“梯度爆炸”的问题。_在人工神经网络研究的初始阶段,辛顿针对训练过程中常出现的梯度消失现象, 提供相

随便推点

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

vscode打开markdown文件 不显示图片 预览markdown文件_vscodemarkdown图片无法显示-程序员宅基地

文章浏览阅读3.2k次,点赞3次,收藏4次。vscode打开markdown文件 不显示图片 预览markdown文件_vscodemarkdown图片无法显示

推荐文章

热门文章

相关标签