【Linux0.11代码分析】03 之 setup.s 启动流程

news/2024/11/28 23:38:39/

【Linux0.11代码分析】03 之 setup.s 启动流程

  • 一、boot\setup.s


系列文章如下:

系列文章汇总:《【Linux0.11代码分析】之 系列文章链接汇总(全)》
.
1.《【Linux0.11代码分析】01 之 代码目录分析》
2.《【Linux0.11代码分析】02 之 bootsect.s 启动流程》
3.《【Linux0.11代码分析】03 之 setup.s 启动流程》
4.《【Linux0.11代码分析】04 之 head.s 启动流程》



在前文中,我们知道,bootsect.ssetup.ssystem 加载到内存后,
通过 jmpi 0,SETUPSEG 指令跳转到 0x90200 开始执行 setup.s程序,把CPU交到 setup.s 手中。

setup.s 主要功能如下:

  1. ROM BIOS 中获取内存、磁盘等系统数据,并将这些数据保存到0x90000-0x901FF,它会覆盖掉 bootsect.s 程序所在的地方。
  2. system模块从0x10000 - 0x8FFFF 整块向下移动到内存绝对地址 0x00000
  3. 加载中断描述符表寄存器(idtr)和全局描述符表寄存器(gdtr
  4. 开启A20地址线,重新设置两个中断控制芯片8259A,将硬件中断号重新设置为0x20 - 0x2F
  5. 设置CPU的控制寄存器CRO(机器状态字),从而进入32位保护模式运行
  6. 跳转到位于system模块最前面部分的head.s程序运行

所取得的参数如下:

内存地址长度(字节)名称描述
0x900002光标位置列号(0x00-最左端),行号(0x00-最顶端)
0x900022扩展内存数系统从 1M 开始的扩展内存数值(KB)
0x900042显示页面当前显示页面
0x900061显示模式
0x900071字符列数
0x900082
0x9000A1显示内存显示内存(0x00-64k,0x01-128k,0x02-192k,0x03=256k)
0x9000B1显示状态0x00-彩色,I/O=0x3dX;0x11-单色,I/O=0x3bX
0x9000C2特性参数显示卡特性参数
0x9008016硬盘参数表第 1 个硬盘的参数表
0x9009016硬盘参数表第 2 个硬盘的参数表(如果没有,则清零)
0x901FC2根设备号根文件系统所在的设备号(bootsec.s 中设置)

一、boot\setup.s

详细工作如下:

  1. 配置数据段寄存器 ds = ax = 0x9000
  2. 读取当前光标位置, BIOS中断0x10, 功能号ah=0x03, 然后将当前光标位置保存在 0x90000 第一个字节处
  3. 获取扩展内存的大小值, BIOS中断0x15, 功能号是ah=0x88
  4. 获取显示当前的显示模式,调用BIOS中断0x10,功能号ah=0x0f
  5. 检查显示方式(EGA/VGA)并取参数, 调用BIOS中断0x10,附加功能选择-取方式信息, 功能号:ah=0x12,bl=0x10
  6. 获取第一个硬盘的硬盘参数表,第1个硬盘参数表的首地址是中断向量0x41的向量值,保存在0x90080
  7. 获取第二个硬盘的硬盘参数表,第2个硬盘参数表的首地址是中断向量0x46的向量值,保存在0x90090
  8. 检查系统是否存在第2个硬盘,如不存在,则将 0x90090地址的第二个表清零
  9. 禁止中断
  10. system0x10000-0x8FFFF 移动到 0x00000-0x7FFFF (512k)
  11. 加载中断描述符表(idt)寄存器和 全局描述符表(gdt)寄存器
  12. 使能 A20 地址线
  13. 对中断进行重新编程,将它们保存在 intel保留的硬件中断后面,位于0x20-0x2F
  14. 配置进入32位保护模式运行,跳转到0x00000地址,开始运行system模块的head.s

# linux-0.11\boot\setup.sINITSEG  = 0x9000	// 原 bootsect.s 所处的段 0x90000 (576kb)    	! we move boot here - out of the way
SYSSEG   = 0x1000	// 当前system 模块所处的段 0x10000 (64kb)   	! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020	// 当前 setup.s 程序所处的段 0x90200 (576.5kb)	! this is the current segment.globl begtext, begdata, begbss, endtext, enddata, endbss	// 定义全局标志符
.text			// 文本段
begtext:
.data			// 数据段
begdata:
.bss			// 堆栈段
begbss:
.text			// 文本段entry start		// 程序入口函数 start
start:// 1. 配置数据段寄存器  ds = ax = 0x9000mov	ax,#INITSEG				! this is done in bootsect already, but...mov	ds,ax// 2. 读取当前光标位置, BIOS中断0x10, 功能号ah=0x03, 然后将当前光标位置保存在 0x90000 第一个字节处// 返回:ch=扫描开始线,cl=扫描结束线, dh=行号(0x00 是顶端),dl=列号(0x00 是左边)。 mov	ah,#0x03	! read cursor posxor	bh,bhint	0x10		! save it in known place, con_init fetchesmov	[0],dx		// 0x90000: 列号(0x00 是左边),   0x90001:行号(0x00 是顶端)// 3. 获取扩展内存的大小值, BIOS中断0x15, 功能号是ah=0x88// 返回ax = 从 0x100000(1M)处开始的扩展内存大小(KB), 若出错则 CF 置位,ax = 出错码mov	ah,#0x88int	0x15mov	[2],ax		// 0x90002(1字)存放从 0x100000(1M)处开始的扩展内存大小(KB)// 4. 获取显示当前的显示模式,调用BIOS中断0x10,功能号ah=0x0f// 返回:ah=字符列数,al=显示模式,bh=当前显示页mov	ah,#0x0fint	0x10mov	[4],bx		// 0x90004(1字)存放当前页  ! bh = display pagemov	[6],ax		// 0x90006显示模式, 0x90007 字符列数   ! al = video mode, ah = window width// 5. 检查显示方式(EGA/VGA)并取参数, 调用BIOS中断0x10,附加功能选择-取方式信息, 功能号:ah=0x12,bl=0x10// 返回:bh = 显示状态 (0x00 - 彩色模式,I/O 端口=0x3dX)  (0x01 - 单色模式,I/O 端口=0x3bX) //  	 bl = 安装的显示内存 (0x00 - 64k, 0x01 - 128k, 0x02 - 192k, 0x03 = 256k)// 		 cx = 显示卡特性参数mov	ah,#0x12mov	bl,#0x10int	0x10mov	[8],ax			// 0x90008 mov	[10],bx			// 0x9000A 安装的显示内存,  0x9000B 显示状态mov	[12],cx			// 0x9000C 显示卡特性参数// 6. 获取第一个硬盘的硬盘参数表,第1个硬盘参数表的首地址是中断向量0x41的向量值,保存在0x90080处mov	ax,#0x0000mov	ds,ax			// 配置 数据段寄存器 ds = ax = 0x0000lds	si,[4*0x41]		// 取中断向量 0x41的值 ,即hd0 参数表的地址 ds:simov	ax,#INITSEG		// 配置 段基址为 ex = ax = 0x9000mov	es,axmov	di,#0x0080		// 传输的目的地址: 0x9000:0x0080    es:dimov	cx,#0x10		// 共转输 0x10 字节repmovsb// 7. 获取第二个硬盘的硬盘参数表,第2个硬盘参数表的首地址是中断向量0x46的向量值,保存在0x90090处mov	ax,#0x0000mov	ds,ax			// 配置 数据段寄存器 ds = ax = 0x0000lds	si,[4*0x46]		// 取中断向量 0x416的值 ,即hd1 参数表的地址 ds:simov	ax,#INITSEG		// 配置 段基址为 ex = ax = 0x9000mov	es,axmov	di,#0x0090		// 传输的目的地址: 0x9000:0x0090    es:dimov	cx,#0x10		// 共转输 0x10 字节repmovsb// 8. 检查系统是否存在第2个硬盘,如不存在,则将 0x90090地址的第二个表清零// 利用 BIOS 中断调用 0x13 的取盘类型功能, 功能号 ah = 0x15// 输入:dl = 驱动器号(0x8X 是硬盘:0x80 指第 1 个硬盘,0x81 第 2 个硬盘)
// 输出:ah=类型码;00没有这个盘,CF置位;01是软驱,没有change-line支持;02是软驱(或其它可移动设备),有change-line支持;03是硬盘mov	ax,#0x01500mov	dl,#0x81int	0x13jc	no_disk1cmp	ah,#3			// 判断 ah==3 ,判断disk1是否存在je	is_disk1
no_disk1:mov	ax,#INITSEG		// 第2个硬盘不存在,则对第2个硬盘表清零。mov	es,axmov	di,#0x0090mov	cx,#0x10mov	ax,#0x00repstosb
is_disk1:! now we want to move to protected mode ...// 9. 禁止中断cli			! no interrupts allowed !// 10. 将system从0x10000-0x8ffff 移动到 0x00000-0x7ffff(512k)mov	ax,#0x0000cld				// 将标志寄存器Flag的方向标志位DF清零 ! 'direction'=0, movs moves forward
do_move:mov	es,ax		// 目的地址 es:di = 0x0000:0x0 add	ax,#0x1000	// ax = ax + 0x1000cmp	ax,#0x9000	// 判断是否等于 0x9000, 如果相等则说明移动完毕jz	end_movemov	ds,ax		// 源地址 ds:si = 0x1000:0x0sub	di,di		sub	si,simov 	cx,#0x8000		// 移动0x8000字(64byte)repmovswjmp	do_move// 11. 加载中断描述符表(idt)寄存器和 全局描述符表(gdt)寄存器
end_move:// 将数据段寄存器 ds = ax = 0x9020mov	ax,#SETUPSEG			! right, forgot this at first. didn't work :-)mov	ds,ax// 加载中断描述符表(idt)寄存器,// 它的操作数是6个字节,0-1 字节是描述符表的长度值(字节);2-5 字节是描述符表的 32 位线性基地址(首地址)// 中断描述符表中的每一个表项(8 字节)指发生中断时需要调用的代码的信息,与中断向量有些相似,但要包含更多的信息lidt	idt_48				! load idt with 0,0---------------->+	idt_48:+		.word	0			! idt limit=0+		.word	0,0			! idt base=0L<----------------	// 加载全局描述符表(gdt)寄存器// 全局描述符表中的每个描述符项(8字节)描述了保护模式下数据和代码段(块)的信息// 包括段的最大长度限制(16位)、段的线性基址(32位)、段的特权级、段是否在内存、读写许可以及其它一些保护模式运行的标志。lgdt	gdt_48				! load gdt with whatever appropriate---------------->+	gdt:+		.word	0,0,0,0		! dummy+	+		.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)+		.word	0x0000		! base address=0+		.word	0x9A00		! code read/exec+		.word	0x00C0		! granularity=4096, 386+	+		.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)+		.word	0x0000		! base address=0+		.word	0x9200		! data read/write+		.word	0x00C0		! granularity=4096, 386++	gdt_48:+		.word	0x800		! gdt limit=2048, 256 GDT entries+		.word	512+gdt,0x9	! gdt base = 0X9xxxx<----------------	! that was painless, now we enable A20// 12. 使能 A20 地址线call	empty_8042			// 等待输入缓冲器为空--------------->+	! This routine checks that the keyboard command queue is empty No timeout is used - +	! if this hangs there is something wrong with the machine, and we probably couldn't proceed anyway.+	empty_8042:+		.word	0x00eb,0x00eb+		in	al,#0x64		! 8042 status port+		test	al,#2		! is input buffer full?+		jnz	empty_8042		! yes - loop+		ret<---------------// 0xD1 命令码-表示要写数据到8042的P2端口, P2端口的位1用于A20线的选通,数据要写到0x60口mov	al,#0xD1				! command writeout	#0x64,alcall	empty_8042			// 等待输入缓冲器为空mov	al,#0xDF				// 选通 A20 地址线的参数  	! A20 onout	#0x60,alcall	empty_8042			// 等待输入缓冲器为空// 13. 对中断进行重新编程,将它们保存在 intel保留的硬件中断后面,位于0x20-0x2F// 0x11 表示初始化命令开始, 是 ICW1 命令字,表示边沿触发、多片 8259级连、最后要发送 ICW4 命令字。		mov	al,#0x11		! initialization sequence// 发送到 8259A 主芯片out	#0x20,al		! send it to 8259A-1	.word	0x00eb,0x00eb		// 延时	! jmp $+2, jmp $+2// 再发送到 8259A 从芯片out	#0xA0,al		! and to 8259A-2.word	0x00eb,0x00eb		// 延时// 送主芯片 ICW2 命令字,起始中断号,要送奇地址mov	al,#0x20		! start of hardware int's (0x20)out	#0x21,al.word	0x00eb,0x00eb		// 延时// 送从芯片 ICW2 命令字,起始中断号mov	al,#0x28		! start of hardware int's 2 (0x28)out	#0xA1,al.word	0x00eb,0x00eb		// 延时//  送主芯片 ICW3 命令字,主芯片的 IR2 连从芯片 INTmov	al,#0x04		! 8259-1 is masterout	#0x21,al.word	0x00eb,0x00eb		// 延时// 送从芯片 ICW3 命令字,表示从芯片的 INT 连到主芯片的 IR2 引脚上。 mov	al,#0x02		! 8259-2 is slaveout	#0xA1,al.word	0x00eb,0x00eb		// 延时// 送两个芯片的 ICW4 命令字。8086 模式;普通 EOI方式,需发送指令来复位。初始化结束,芯片就绪mov	al,#0x01		! 8086 mode for bothout	#0x21,al.word	0x00eb,0x00ebout	#0xA1,al.word	0x00eb,0x00eb// 当前屏蔽所有中断请求mov	al,#0xFF		! mask off all interrupts for nowout	#0x21,al.word	0x00eb,0x00ebout	#0xA1,al// 14. 配置进入32位保护模式运行,跳转到0x00000地址,开始运行system模块的head.smov	ax,#0x0001				! protected mode (PE) bit// 加载机器状态字(lmsw - Load Machine Status Word), 也称控制寄存器 CR0,其bit0 置1将导致CPU工作在保护模式lmsw	ax					! This is it!// 配置为特权级为系统级、使用全局描述符,索引项为0, 偏移为0, 也就是跳转到 0x00000的地址开始运行jmpi	0,8					! jmp offset 0 of segment 8 (cs)// 段选择符长度为 16 位(2 字节);// 位 0-1 表示请求的特权级 0-3, linux 操作系统只用到两级:0 级(系统级)和 3 级(用户级);// 位 2 用于选择全局描述符表(0)还是局部描述符表(1)// 位 3-15 是描述符表项的索引,指出选择第几项描述符// 所以段选择符8(0b0000,0000,0000,1000)表示请求特权级 0、使用全局描述符表中的第 1 项

http://www.ppmy.cn/news/61773.html

相关文章

使用Android Studio 利用极光推送SDK 制作手机 APP 实现远程测试技术 (第一部)

总参考文章&#xff1a;https://blog.csdn.net/qq_38436214/article/details/105073213 Android Studio 安装配置教程 - Windows(详细版) 1.JDK 安装与环境变量配置(Win10详细版) 《jdk-8u371-windows-i586.exe》 https://blog.csdn.net/qq_38436214/article/details/1050710…

2023爱分析·低代码开发平台市场厂商评估报告:数聚股份

1. 研究范围定义 随着数字化转型浪潮的推进&#xff0c;企业的数字化应用开发需求快速爆发。低代码作为一种“软件开发新范式”&#xff0c;凭借其可视化、快速构建数字化应用的能力&#xff0c;帮助企业提升数字化应用开发效率、降低开发门槛&#xff0c;深度拥抱数字化转型。…

探究Android插件化开发的新思路——Shadow插件化框架

Shadow插件化框架是什么&#xff1f; Shadow是一种Android App的插件化框架&#xff0c;它利用类似于ClassLoader的机制来实现应用程序中的模块化&#xff0c;并让这些模块可以在运行时灵活地进行加载和卸载。Shadow框架主张将一个大型的Android App拆分成多个小模块&#xff…

Blender 建模练习-锁链

目录 1.1.1 贝塞尔圆1.2 阵列修改器1.3 阵列修改器 物体偏移1.4 添加贝塞尔曲线1.5 曲线修改器 1. 本次练习主要使用到阵列修改器、贝塞尔曲线、空物体 1.1 贝塞尔圆 把贝塞尔圆进行缩放&#xff0c;然后在物体数据属性|几何数据|倒角|设置倒角深度为0.05 1.2 阵列修改器 …

3D开发工具HOOPS最新解析合集:助力实现web端高性能模型渲染

一、3D技术为创新提供强大助力&#xff08;1&#xff09;3D专家提供专属技术支持服务 不管您想搭建桌面、WEB或者移动端APP应用&#xff0c;技术领先全球的HOOPS Platform组件都可以为您提供弹性的3D集成架构&#xff0c;同时&#xff0c;一批可信任的工业领域3D技术专家也将为…

2018年下半年软件设计师下午试题

试题四&#xff08;15分&#xff09; 给定一个字符序列Bb1b2….bn&#xff0c;其中bi∈{A,C,G,U}。B上的二级结构是一组字符对集合S{(bi,bj)},其中i,j∈{1,2,….,n}&#xff0c;并满足以下四个条件&#xff1a; &#xff08;1&#xff09;S中的每对字符是(A,U),(U,A),(C,G)和…

ESP32-C2开发板简介

C2是一个芯片采用4毫米x 4毫米封装&#xff0c;与272 kB内存。它运行框架&#xff0c;例如ESP-Jumpstart和ESP造雨者&#xff0c;同时它也运行ESP-IDF。ESP-IDF是Espressif面向嵌入式物联网设备的开源实时操作系统&#xff0c;受到了全球用户的信赖。它由支持Espressif以及所有…

让你的C++代码变得更加高效和优雅的技巧(第一集)

​ C 是一门强大的编程语言&#xff0c;它被广泛应用于各种领域&#xff0c;包括游戏开发、系统编程、嵌入式系统等。但是&#xff0c;C 也是一门复杂的语言&#xff0c;需要程序员有一定的技巧才能写出高效和优雅的代码。在本文中&#xff0c;我们将介绍一些让你的 C 代码变…