【Linux0.11代码分析】02 之 bootsect.s 启动流程

news2024/11/18 15:45:00

【Linux0.11代码分析】02 之 bootsect.s 启动流程

  • 一、boot\bootsect.s
    • 1.1 将bootsect.s 从0x7c00 移动到 0x90000 (512byte)
    • 1.2 使用 int 0x13 中断加载 setup.s 程序到 0x90200
    • 1.3 获取并解析磁盘驱动器的参数
    • 1.4 开始加载 System模块到 0x10000 地址
    • 1.5 确定根文件系统 root_dev=0x306
    • 1.6 跳转到 0x90200 开始执行setup.s程序,它会将system从0x10000,加载到0x0000处


系列文章如下:

1.《【Linux0.11代码分析】01 之 代码目录分析》
2.《【Linux0.11代码分析】02 之 bootsect.s 启动流程》
3.《【Linux0.11代码分析】03 之 setup.s 启动流程》
4.《【Linux0.11代码分析】04 之 head.s 启动流程》



PC 电源打开后,整个开机流程如下:

  1. 80x86结构的CPU将自动进入实模式,从ROM-BIOS的地址(0xFFFF0)开始自动执行BIOS程序代码,
  2. BIOS将执行系统检测,如找到启动设备(一般为软盘或硬盘),并在物理地址0x0 处开始初始化中断向量
  3. 然后BIOS将启动设备的第一个扇区(磁盘引导扇区,512字节)的内容读取写入内存地址0x7c00处,并跳转到0x7c00处。

linux 内核最开始的代码是bootselect.s,它将被BIOS读入内存0x7C00处,
然后,BIOSCPU 交到 Linux手中,就开始执行 bootsect.s代码,正式开始 Linux 之旅


一、boot\bootsect.s

开机后,RAM 内存中各地址数据如下:
在这里插入图片描述
下面,我们来详细分析下代码实现

1.1 将bootsect.s 从0x7c00 移动到 0x90000 (512byte)

# boot\bootsect.s

! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current versions of linux

SYSSIZE = 0x3000	// 单位是clicks(16byte),编译链接后的system 模块的大小为 0x3000*16byte = 0x30000byte = 196608byte = 192kb

! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves iself out of the way to address 0x90000, and jumps there.
// bootsect.s 被bios-startup子程序加载到0x7c00(31k)处,然后将自身移动到0x90000(576k)处,并跳转到那里

! It then loads 'setup' directly after itself (0x90200), and the system at 0x10000, using BIOS interrupts. 
// 然后使用BIOS 中断将setup 直接加载到自已的后面,地址为0x90200(576.5k),将system 加载到0x10000(64k)

// 定义了6个全局标志符,在其他代码中也能看到
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text				// 文本段
begtext:			
.data				// 数据段
begdata:		
.bss				// 堆栈段
begbss:
.text				// 文本段

// setup程序占4个扇区,4 x 512byte = 2048byte = 2kb
SETUPLEN = 4				! nr of setup-sectors
// bootsect 的起始地址为 0x07c00(31kb) (单位 clicks 16byte)
BOOTSEG  = 0x07c0			! original address of boot-sector
// 要将 boot移动到 0x90000(576kb)处
INITSEG  = 0x9000			! we move boot here - out of the way
// setup 程序 从 0x90200(576.5kb) 处开始运行
SETUPSEG = 0x9020			! setup starts here
// system 模块要加载到 0x10000(64kb) 处,加载截止段地址ENDSEG为 0x10000 + 0x30000 = 0x40000(256kb) 
SYSSEG   = 0x1000			! system loaded at 0x10000 (65536).
ENDSEG   = SYSSEG + SYSSIZE		! where to stop loading


! ROOT_DEV:	0x000 - same type of floppy as boot.
!		0x301 - first partition on first drive etc
ROOT_DEV = 0x306	// 指定根文件系统设备是第2个硬盘的第1个分区。
					// 0x300 - /dev/hd0 - 代表整个第 1 个硬盘;
					// 0x301 - /dev/hd1 - 第 1个盘的第 1 个分区;
					// 0x302 - /dev/hd2 - 第 1个盘的第 2 个分区;
					// 0x303 - /dev/hd3 - 第 1个盘的第 3 个分区;
					// 0x304 - /dev/hd4 - 第 1个盘的第 4 个分区;
					
					// 0x305 - /dev/hd5 - 代表整个第 2 个硬盘盘;
					// 0x306 - /dev/hd6 - 第 2个盘的第 1 个分区;
					// 0x307 - /dev/hd6 - 第 2个盘的第 2 个分区;
					// 0x308 - /dev/hd6 - 第 2个盘的第 3 个分区;
					// 0x309 - /dev/hd9 - 第 2个盘的第 4 个分区;

///
//  1. 将bootsect.s 从0x7c00 移动到 0x90000 (512byte)
///
entry _start		// 程序从 _start 处开始执行
_start:
	mov	ax,#BOOTSEG	 	// ax = 0x07c0
	mov	ds,ax			// 数据段寄存器DS = 0x07c0
	mov	ax,#INITSEG		// ax = 0x9000
	mov	es,ax			// 附加段寄存器ES = 0x9000
	mov	cx,#256			// cx = 256
	sub	si,si			// 源地址:		ds:si = 0x07C0 : 0X0000
	sub	di,di			// 目的地址:	es:di = 0x9000 : 0X0000
	rep					// 重复执行 ,直到 cx = 0
	movw				// 移动一个字节
	jmpi	go,INITSEG	// 段间跳转指令 此时 CS = 0x9000
go:	mov	ax,cs			// ax = cs = 0x9000
	mov	ds,ax			// 数据段寄存器 ds = ax = 0x9000
	mov	es,ax			// 段地址寄存器 es = ax = 0x9000
! put stack at 0x9ff00.	// 将堆栈指针SP 指向 0x9ff00 (0x9000 : 0xff00)处,由于代码段移动过了,所以要重新设置堆栈段的位置
	mov	ss,ax			// 栈段寄存器ss = 0x9000
	mov	sp,#0xFF00		// 堆栈指针sp = 0x9ff00      ! arbitrary value >>512

! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.

1.2 使用 int 0x13 中断加载 setup.s 程序到 0x90200

# boot\bootsect.s
///
//  2. 使用 int 0x13 中断加载 setup.s 程序到 0x90200
///
load_setup:
	// dx=0, cx=2, bx=0x200, ax=0x204  (每个扇512byte,0x204 * 512 = 258kb)
	mov	dx,#0x0000					! drive 0, head 0		
	mov	cx,#0x0002					! sector 2, track 0
	mov	bx,#0x0200					! address = 512, in INITSEG
	mov	ax,#0x0200+SETUPLEN			! service 2, nr of sectors
	// 使用 INT13 中断服务程序读取扇516个扇区
	int	0x13						! read it
	// 读取成功,则跳转到 ok_load_setup 标识符开始运行
	jnc	ok_load_setup				! ok - continue
	// 读取失败,则重置 dx=0,ax=0,重新读取运行load_setup 直到成功为止			
	mov	dx,#0x0000
	mov	ax,#0x0000		! reset the diskette
	int	0x13
	j	load_setup

ok_load_setup:

1.3 获取并解析磁盘驱动器的参数

# boot\bootsect.s
///
//  3. 获取并解析磁盘驱动器的参数
///
// 获取磁盘驱动器的参数,物别是每道的扇区数量
! Get disk drive parameters, specifically nr of sectors/track
	// 驱动器号 dl = 0x0
	mov	dl,#0x00
	// ax = 0x0800 ,  ah=0x08 说明获取驱动参数, al=0x00
	mov	ax,#0x0800		! AH=8 is get drive parameters
	// 触发0x13中断,传参 ah=0x08, dl=0x00
	int	0x13	
	// 配置最大磁道号低八位 ch=0x00,  cx中: 最大磁道号高2位(位6-7) ,每磁道最大扇区数低6位(位0-5)
	mov	ch,#0x00	
	seg cs				// 以下代码的操作数在 cs段寄存器 所指向的段中
	mov	sectors,cx		// 保存 cx 每磁道最大扇区数 到 sectors 中
	mov	ax,#INITSEG		// ax = 0x9000  
	mov	es,ax			// es = 0x9000 , 由于上面改过了段地址,重新配置段地址寄存器 es = ax = 0x9000

! Print some inane message	// 显示信息('Loading system ...'回车换行,共24个字符)
	mov	ah,#0x03			// 读入光标的位置 		 ! read cursor pos
	xor	bh,bh				// 清空 bh = 0x0
	int	0x10				// 触发 int 0x10中断
	
	mov	cx,#24				// cx = 24,共显示24个字符
	mov	bx,#0x0007			// BL 为属性,字符显示属性    ! page 0, attribute 7 (normal)
	mov	bp,#msg1			// bp 指向要显示的字符串: .ascii "Loading system ..."
	--------------->
	+	msg1:
	+		.byte 13,10
	+		.ascii "Loading system ..."
	+		.byte 13,10,13,10
	<---------------
	mov	ax,#0x1301			// ah=0x13 显示字符串,al=0x01彩色显示   ! write string, move cursor
	int	0x10				// 触发 int 0x10中断开始显示

1.4 开始加载 System模块到 0x10000 地址

# boot\bootsect.s
///
//  4. 开始加载 System模块到 0x10000 地址
///
! ok, we've written the message, now we want to load the system (at 0x10000)

	mov	ax,#SYSSEG			// ax = 0x10000
	mov	es,ax				// ex = 0x10000    ! segment of 0x010000
	call	read_it			// 调用子函数 read_it 读取system ,参数为 es
	call	kill_motor		// 关闭软驱的马达

// 检查要使用哪个根文件系统设备(根设备),如果已经指定了设备(!=0),则直接用指定的,
// 否则需要根据BIOS报告的每磁道扇区数来确定使用/dev/PS0(2,28), 还是/dev/at0(2,8)

===================================================================================================
! This routine loads the system at address 0x10000, making sure no 64kB boundaries are crossed. 
! We try to load it as fast as possible, loading whole tracks whenever we can.
!
! in:	es - starting address segment (normally 0x1000)
!
sread:	.word 1+SETUPLEN	// 当前磁道中已读的扇区数。开始时已经读进 1 扇区的引导扇区   ! sectors read of current track
head:	.word 0				// 当前磁头号    	! current head
track:	.word 0				// 当前磁道号	! current track

read_it:
	mov ax,es
	test ax,#0x0fff
die:	jne die			! es must be at 64kB boundary
	xor bx,bx		! bx is starting address within segment
rp_read:
	mov ax,es
	cmp ax,#ENDSEG		! have we loaded all yet?
	jb ok1_read
	ret
ok1_read:
	seg cs
	mov ax,sectors
	sub ax,sread
	mov cx,ax
	shl cx,#9
	add cx,bx
	jnc ok2_read
	je ok2_read
	xor ax,ax
	sub ax,bx
	shr ax,#9
ok2_read:
	call read_track
	mov cx,ax
	add ax,sread
	seg cs
	cmp ax,sectors
	jne ok3_read
	mov ax,#1
	sub ax,head
	jne ok4_read
	inc track
ok4_read:
	mov head,ax
	xor ax,ax
ok3_read:
	mov sread,ax
	shl cx,#9
	add bx,cx
	jnc rp_read
	mov ax,es
	add ax,#0x1000
	mov es,ax
	xor bx,bx
	jmp rp_read

read_track:
	push ax
	push bx
	push cx
	push dx
	mov dx,track
	mov cx,sread
	inc cx
	mov ch,dl
	mov dx,head
	mov dh,dl
	mov dl,#0
	and dx,#0x0100
	mov ah,#2
	int 0x13
	jc bad_rt
	pop dx
	pop cx
	pop bx
	pop ax
	ret
bad_rt:	mov ax,#0
	mov dx,#0
	int 0x13
	pop dx
	pop cx
	pop bx
	pop ax
	jmp read_track

!/*
! * This procedure turns off the floppy drive motor, so
! * that we enter the kernel in a known state, and
! * don't have to worry about it later.
! */
kill_motor:
	push dx
	mov dx,#0x3f2
	mov al,#0
	outb
	pop dx
	ret

sectors:
	.word 0
	
.org 508
root_dev:
	.word ROOT_DEV
boot_flag:
	.word 0xAA55

.text
endtext:
.data
enddata:
.bss
endbss:

1.5 确定根文件系统 root_dev=0x306

# boot\bootsect.s
///
//  5. 确定根文件系统 root_dev=0x306
///
// 在Linux 中软驱的主设备号是2,次设备号 = type*4 + nr,其中nr为0-3 分别对应软驱A、B、C 或D;
// type 是软驱的类型(2: 1.2M  或 7: 1.44M 等)
// 因为7*4 + 0 = 28,所以 /dev/PS0 (2,28)指的是1.44M A 驱动器,其设备号是0x021c
// 同理2*4 + 0 = 8, 所以 /dev/at0 (2,8)指的是1.2M A 驱动器,其设备号是0x0208
! After that we check which root-device to use. If the device is defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending on the number of sectors that the BIOS reports currently.

	seg cs				// 以下代码的操作数在 cs段寄存器 所指向的段中
	mov	ax,root_dev		// ax = ROOT_DEV = 0x306
	cmp	ax,#0			
	jne	root_defined	// 由于已经指定 root_dev=0x306, 跳转root_defined	
	seg cs
	mov	bx,sectors		
	mov	ax,#0x0208		// 判断软驱类型 是否为1.2Mb    ! /dev/ps0 - 1.2Mb
	cmp	bx,#15
	je	root_defined
	mov	ax,#0x021c		// 判断软驱类型 是否为1.44Mb    ! /dev/PS0 - 1.44Mb
	cmp	bx,#18
	je	root_defined
undef_root:				// 如果找不到根文件系统设备,则在此处halt死机
	jmp undef_root
root_defined:			
	seg cs
	mov	root_dev,ax		// root_dev = 0x0306

1.6 跳转到 0x90200 开始执行setup.s程序,它会将system从0x10000,加载到0x0000处

# boot\bootsect.s
///
//  6. 跳转到 0x90200 开始执行setup.s程序,它会将system从0x10000,加载到0x0000处
///
! after that (everyting loaded), we jump to the setup-routine loaded directly after the bootblock:
	jmpi	0,SETUPSEG	// 跳转到  0x90200 开始执行 setup.s程序




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/492553.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络之应用层

文章目录 应用层1. 应用层协议原理1.1 网络应用程序体系结构1.2 进程通信1.3 可供程序使用的运输服务1.4 因特网提供的运输服务1.5 应用层协议 2.Web应用和HTTP协议2.1 HTTP概况2.2 非持久连接和持久连接2.3 HTTP报文格式2.4 用户服务器的交互&#xff1a;cookie2.5 Web缓存器 …

数字医疗算法应用创新大赛-食品与疾病关系预测算法赛道top5方案分享

一、比赛地址和背景 1.1 比赛地址 比赛地址 1.2 数据说明及任务 本次算法赛将提供超过 23.5W 的食物、疾病对应关系及其量化得分&#xff0c;其中食物特征超过 200 个&#xff0c;疾病特征由 3 种不同的方式抽取&#xff0c;累积超过 4000 个特征信息。初赛为 0、1 二分类预…

递归递推练习题

1.用递归的方法123…N的值&#xff08;in:5,out:15&#xff09; 2.输出斐波那契数列的第N项&#xff0c;0&#xff0c;1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13…(in:3,out:1) 3.求n!&#xff08;in:5,out:120&#xff09;n!123……

STM32启动/下载模式

启动模式 主闪存存储器区存放用户设计的代码&#xff0c;代码烧录到这里&#xff0c;一般情况在正常工作时需要把STM32设置为从这里启动。 系统存储器区用来实现ISP功能的。 内嵌SRAM区用来实现调试器调试功能的。 ISP ISP一般由PC机通过串口把bin/hex文件直接烧写到单片机内…

java并发编程之美第五章读书笔记

java并发包中并发List源码剖析 介绍 CopyOnWriteArrayList 线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)进行的,也就是写时复制策略 类图 每一个对象里面有一个array数组进行存放具体的元素,ReentrantLock独占锁对象用来保证同时只有一个线程对…

Maven中optional标签详解

这一篇文章应该是全网讲解optional最细致的&#xff0c;因为我都是拿我自己的试验来证明&#xff0c;从试验当中也发现了很多出乎意外的知识&#xff01;感兴趣的跟着小编一块来学习呀&#xff01; 目录 一、前言二、依赖传递代码演示三、是否会影响父子工程之间的依赖继承呢&a…

精致女孩必备的6款APP,内外兼修,提升气质

自立自强、敢于追求、内外兼修&#xff0c;这样的女孩不管在哪个时代都是非常有魅力的&#xff01; 在我看来&#xff0c;自律、上进、情商高、会打扮、会赚钱、独立、落落大方&#xff0c;这才是精致女孩该有的样子。 不墨迹&#xff0c;速速揭开6款宝藏APP的面纱&#xff0…

石头科技2022年营收实现双位数增长,以技术实力打响创新价值战

近日&#xff0c;石头科技披露了2022年度财务报告&#xff0c;报告显示&#xff0c;在在较大内外部压力下&#xff0c;石头科技2022年营收依然实现双位数增长&#xff0c;且境内外销售收入平稳增长。 该公司在近年来不断完善其产品矩阵&#xff0c;目前已推出手持无线吸尘、商…

数据结构 (入门必看)

一、 预备知识 &#xff08;一&#xff09;数据结构前言 1、学习C语言是如何写程序,学习数据结构如何简洁高效的写程序 2、遇到一个实际问题&#xff0c;需要写程序&#xff0c;需要解决两个方面的问题 1)如何表达数据之间的逻辑规律以及如何将数据存储到计算机中 数据结构…

多数据源 使用 flyWay 进行数据库管理

文章目录 前言多数据源 使用 flyWay 进行数据库管理1. 环境2. flyway版本 与 MySQL 版本 对应关系3. flyway 脚本文件命名方式4. flyway工作流程5. 知识点补充6. 集成的时候常见错误6.1. user_variables_by_thread没有访问权限6.2. MySQL不支持Flyway社区版&#xff0c;只支持F…

68元工业级双核A7,全新T113核心板震撼上市!

万象奥科全新T113核心板震撼上市&#xff0c;仅68元、工业级品质、双核A7处理器、外设丰富&#xff0c;诠释极致性价比&#xff01; 参数速递 1.全志T113工业级处理器&#xff0c;主频1.2GHz 2.双核Cortex-A7玄铁C906 RISC-V HiFi4 DSP异构多核 3.内存128MB/256MB/512MB&a…

【Kafka】概述与集群部署

文章目录 Kafka概述定义应用场景缓冲/削峰解耦异步通信 应用模式点对点模式发布/订阅模式 基础架构 Kafka集群部署集群规划下载解压修改配置文件分发安装包hadoop103、hadoop104修改配置文件配置环境变量启动集群先启动Zookeeper集群然后启动Kafka 关闭集群 集群启停脚本脚本编…

QT自学笔记2:使用介绍(函数)

一、 setAttribute&#xff08;按F1&#xff09;—>void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on true) —>Qt::WidgetAttribute attribute&#xff08;有一个属性&#xff09; ----> p->setAttribute(Qt::WA_DeleteOnClose); MainWind…

眼见为实,来瞧瞧MySQL中的隐藏列!

在介绍mysql的多版本并发控制MVCC的过程中&#xff0c;我们提到过mysql中存在一些隐藏列&#xff0c;例如行标识、事务ID、回滚指针等&#xff0c;不知道大家是否和我一样好奇过&#xff0c;要怎样才能实际地看到这些隐藏列的值呢&#xff1f; 本文我们就来重点讨论一下诸多隐…

ThingsBoard集群部署之k8s

1、概述 今天终于有时间去搞这个啦,拖了很久了,一直没时间,因为我本地没有那么多机器资源,开虚拟机不够,如果租用阿里云服务器,需要有充值的时间,因为这个费用是按小时付费,需要有连贯的时间来搞才行,今天恰好有时间,就开始搞了,弄成功搞出来了,特地写博客记录下来…

Linux基本指令【Linux操作系统】

本文将开启Linux操作系统学习新篇章&#xff0c;欢迎与博主一起交流学习。 目录 一、ls指令 二、pwd指令 三、mkdir与rm指令 四、cd指令 五、touch指令 六、man指令&#xff08;重要&#xff09; 七、cp指令&#xff08;重要&#xff09; 八、mv指令&#xff08;重要&…

keepalived如何手动切换主备

概述 主备部署中使用keepalived可以很方便的实现&#xff0c;安装维护简单&#xff0c;功能稳定。 最近在使用过程中有小的发现&#xff0c;记录一下。 环境 CentOS Linux release 7.9.2009 (Core) keepalived.x86_64 1.3.5-19.el7 安装配置 centos7自带的keepalived版本…

提升自我数据分析能力的根本,是方法论!

很多人问&#xff0c;我是财务&#xff0c;能转行业做BI吗&#xff1f;我该学些什么&#xff1f;该掌握哪些技能&#xff1f;该如何学习&#xff1f;我是学生&#xff0c;在校期间专业不扎实&#xff0c;该怎么办&#xff1f;我是小白&#xff0c;还能学会数据分析吗&#xff1…

第二届“强国青年科学家”获奖者均有海外经历

今天是五四青年节&#xff0c;为了弘扬五四精神&#xff0c;知识人网小编本期专门介绍10名第二届“强国青年科学家”获奖者。特别提示&#xff1a;这些科学家均具有海外留学或研究经历。 五四精神的核心内容为“爱国、进步、民主、科学”。 进一步弘扬科学精神&#xff0c;营造…

找不到vcruntime140.dll,无法继续执行代码,解决方法分享

找不到vcruntime140.dll,无法继续执行代码&#xff1f;vcruntime140.dll 是 Visual Studio 2015 运行库的 Dynamic Link Library 文件&#xff0c;许多 Windows 应用程序需要它才能正常运行。当你尝试安装或运行某些应用程序时&#xff0c;有时可能会遇到找不到 vcruntime140.d…