11 阶段总结一:详解OS的引导启动过程(含Bochs模拟软件的使用)

0 前文总结

前面10篇文章重点讲述了两个实例(一个引导程序boot.s的启动、两个任务A和B的轮流切换)、
Linux0.12内核的boot目录下的bootsect.S/setup.S/head.s三个引导启动程序以及Linux内核体系结构和原理,
最终将控制权交给了内核system模块的main程序(执行初始化工作)。
摘要:这篇文章主要梳理自PC上电后如何加载内核到物理内存的一系列过程。

1 BIOS的自述

1.1 BIOS的“基本”体现在哪里?
	首先明确BIOS的职责:初始化硬件、自检、初始化中断向量表,寻址启动设备的MBR进而转移控制权
	BIOS会初始化全部硬件吗?其实不然,BIOS只有64KB,能力很有限。因此,实际上,BIOS只是初始化
	主要的硬件(如显卡、内存等)(注意:硬件自己提供了初始化调用例程,BIOS只是建立中断向量表关联这些
	例程,实际只需调用即可),这就是BIOS为啥称为“基本”的原因
1.2 BIOS在哪里?谁唤醒了BIOS?
	BIOS存在ROM只读存储器中,硬件厂商已固化在硬件上,而PC上电后,CPU的cs:ip寄存器被强制初
	始化为0xF000:0xFFF0,由于开机默认在实地址模式,故实际地址为( cs << 4 + ip ) = 0xFFFF0,即BIOS
	的入口地址,BIOS第一天指令是远跳转,如下图

BIOS的远跳转

1.3 BIOS执行后的下一站--MBR,为啥加载MBR到0x7c00?
	MBR——主引导记录,位于启动设备的0盘0磁道1扇区(CHS方式的磁盘编址,即磁头、柱面、扇区可确定
	磁盘的任意位置)。BIOS在找到启动设备的MBR(最后两字节为0x55AA,标志着此扇区有引导程序存在)后,
	将MBR加载到内存的0x7c00处,“0x7C00”最早出现在 IBM 公司出产的第一台个人电脑(1981.8) PC5150 的ROM BIOS的
	INT19H 中断处理程序中,由于MBR不能覆盖其它的程序,也不能提前被其它程序覆盖,在当时只有32KB
	内存的空间,BIOS占512B,此外BIOS也需要栈空间,故1KB分给BIOS足够了,于是32KB - 1 KB = 0x800 
	- 0x0400 = 0x7c00, 因此 0x7c00就确定为MBR放置的开始地址。
拓展一点:4G的内存条为哈显示<4GB?
	这是由于地址总线位数一定,即可寻址空间一定,CPU还要把部分空间分配给外设,如键盘、显卡、鼠标等这些硬件,这些硬件
	是都有自己的内存空间,CPU要访问它们,肯定需通过外部地址总线访问,因此,必须分配一部分空间给他们。
	此外,内存条中也有这些外设的内存映射,一方面可供CPU像访问内存一样访问这些硬件,另一方面,这部分空间可作为内核访问
	硬件的高速缓冲区,只需对相应控制器发送命令即可读取硬件的内容缓冲在这里,也避免了内核因访问繁多硬件种类而增加内核负担。

2 MBR的面目

说到MBR,肯定要牵扯到它的兄弟姐们:EBR、OBR(它的前世:DBR)
2.1 MBR的内容:
	(1)446 字节的引导程序及参数; (2)64 字节的分区表(可容纳4个分区);(3)2 字节结束标记 0x55 和 0xaa
	分区表每项有一个活动分区标记,0x80 表示此分区上有引导程序,0 表示没引导程序,由此从4个分区找到OS的加载器(引导程序)
2.2 DBR——OBR的前世:
	(1)跳转指令,使 MBR 跳转到引导代码(2)厂商信息、DOS 版本信息(3)BIOS 参数块 BPB,即 BIOS Parameter Block
	(4)操作系统引导程序; (5)结束标记 0x55 和 0xaa
2.3 OBR——即,操作系统的加载器(引导程序)
	OBR 扇区的前 3个字节存放了跳转指令,该起始处的跳转指令马上将处理器带入操作系统引导程序,
	从此 MBR 完成了交接工作,	以后便是内核的天下了。 
	注意:OBR 中开头的跳转指令跳往的目标地址并不固定,这是由所创建的文件系统决定的
2.4 EBR
	当初为了解决分区数量限制的问题才有了扩展分区,EBR 是扩展分区中为了兼容 MBR 才提出的概念,
	主要是兼容 MBR 中的分区表。
控制权流向:BIOS-->MBR-->主分区的OBR 或 子拓展分区EBR中的OBR
最后,三者的位置关系如下图所示

MBR、EBR、OBR位置关系

3 bochs模拟系统的基本使用

3.0 bochs的简介
相比其它模拟软件(如VMware Workstation、Virtual PC),bochs模拟软件能完全仿真Intel x86计算机的程序(即仿真所有执行指令)
出身:1994年由Kevin Lawton采用C++语言开发,官网:http://bochs.sourceforge.net
前提:需在X11环境运行。因此在Linux系统中必须已经安装了X Window系统才能使用Bochs
最大优势:完全仿真Intel x86硬件环境  +  可调试(bochs自带的 或 可用 GDB 调试(前提需设置))
3.1 *.bxrc配置文件选项解析(重点)
1)  msgs: xxx  设定模拟系统的内存大小,默认32M
2) floppya: 第一个软驱,floppyb第二个软驱,其值为指向一个可引导的"磁盘"(应使用设备名称(Linux)或
		  驱动器号(window))或"磁盘映像文件"
如:floppya: 1_44=/dev/fd0, status=inserted			# Linux下直接访问1.44MB的A盘
       floppya: 1_44=b:, status=inserted				# win32系统下直接访问1.44MB的B盘
       floppya: 1_44=bootimage.img, status=inserted	# 指向磁盘映像文件bootimage.img
       
3) ata0、ata1、ata2、ata3:启动模拟系统中最多4个ATA通道。对于每个启用的通道,必须指明两个IO基地址和一个中断请求号。
					   默认情况下只有ata0是启用的,并且参数默认为下面所示的值:
	ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14	
	ata1: enabled=1, ioaddr1=0x170,ioaddr2=0x370, irq=15
	ata2: enabled=1, ioaddr1=0x1e8,ioaddr2=0x3e0, irq=11
	ata3: enabled=1, ioaddr1=0x168,ioaddr2=0x360,irq=9
	(备注:ATA通道也叫“并口”,主要是连接硬盘和光驱的通道,现已被串口替代,但未兼容仍保留有1个并口(1个并口可连接两个设备))
4) ata0-master ( ata0-salve )
	ata0-master 指明模拟系统中第一个ATA通道(0通道)上连接的第一个ATA设备(硬盘或CDROM等)
	ata0-slave  指明第一个ATA通道上连接的第2个ATA设备
	如:ata0-master:type=disk, path=hd.img, mode=flat, cylinders=306, heads=4, spt=17, translation=none
	(对于disk设备,选项 path  cylinders  heads  spt是必须的(CHS:硬盘的CHS寻址方式,
	可参考https://www.cnblogs.com/joyzhuang/p/4126325.html))
5) boot  :  定义模拟机器中用于引导启动的驱动器,可以指定软盘、硬盘或CDROM,也可使用驱动器号“c” 和 “a”
	如: boot: a    boot: c      boot: floppy   boot: disk   boot: cdrom
6) cpu :  定义模拟系统中仿真的CPU的参数,该选项可带4个参数:COUNT  QUANTUM  RESET_ON_TRIPLE_FAULT 和 IPS
	COUNT:  处理器个数,当编译Bochs软件包选用了支持SMP选项时,可支持最多8个同时运行的线程,反之,只能为1
	QUANTUM:指定一个处理器切换到另一个之前最多可指向的指令数量,仅支持SMP的Bochs执行程序
	RESET_ON_TRIPLE_FAULT:指定当处理器发生三重错误时,需对CPU执行复位操作
	IPS:每秒钟仿真的指令条数,需根据所使用主机性能来设定
	如:cpu : count=1, ips=10000000, reset_on_triple_fault=1
7)log:指定log的路径名以让Bochs记录执行的一些日志信息
3.2 常用调试命令
1)显示
	寄存器:
		r:  普通寄存器(如eax、ebx等待,包括eflags寄存器)
		sreg:显示“段寄存器”
		creg:显示“控制寄存器”
		dreg:显示“调试寄存器”
	堆栈:
		print-stack : 显示堆栈内容
		info cpu:查看cpu部分状态,此外,还有 info program/registers/break
		dump_cpu: 显示cpu全部状态信息
2)流程控制
	c:连续执行
	stepi / si [count]  执行count条指令,默认为1条,可进入函数/中断内部单条运行
	s/stip [count]  执行count条指令,默认1条,并不会进入函数/中断程序内部
	n/next   与s类似
	q/quit : 退出调试和执行
3)断点设置
	vbreak/vb  seg:off		在虚拟地址上设置指令断点
	lbreak/lb	seg off 	pbreak/pb  [*] addr  在物理地址上设置指令断点,其中“*”是为与GDB兼容的可选项
	info break 显示所有断点信息  
	delete/d/del n  删除一个断点
4)内存操作——与GDB 类似
	x /nuf addr  检测位于线性地址addr处的内存内容,若addr不指定,则默认为下一个单元地址
	xp /nuf addr  检查位于物理地址addr处的内存内容
	nuf——n:欲显示内存单元的计数值,默认1, u:单元大小,默认w, f:显示格式:默认x(十六进制)
3.3 创建磁盘映像文件
1)利用Bochs软件自带的Image生成工具——bximage.exe
     如:./bximage  -hd  -mode="flat"  -size=xxx  -q   disk1.img   --->创建硬盘映像文件
     	   ./bximage -fd  [0.16, 0.18, 0.32, 0.36, 0.72, 1.2, 1.44, 1.68, 1.72, or 2.88]   fd1.img  --->创建软盘映像文件,大小固定可选
     	   注意:生成Image文件后,会显示一条用于Bochs配置文件中设置硬盘参数文件的配置信息,记得添加进配置文件
2)在Linux系统下使用dd命令创建Image文件
     如:建立柱面数520、磁头数16、每磁道扇区数63的硬盘映像文件
     	   dd if=/dev/zero  of=hdc.img  bs=512  count=524160 (总扇区数:520 * 16 * 64)
     	   创建1.44MB的软盘映像文件
     	   dd if=/dev/zero  of=diska.img bs=512  count=2880
3)利用WinImage创建DOS格式的软盘Image文件(网上一堆资料可参考,很简单)

4 编写MBR

4.1 mbr.S汇编代码
; 在显示屏当前光标处打印“1 MBR”字符串
; 主引导程序
SECTION MBR vstart=0x7c00	; 指定MBR的虚拟地址为0x7c00
mov ax,cs			; 设置ds、es、ss、fs与cs指向相同,都是0x7c00,方便寻址
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00			; 设置栈顶指针指向0x7c000
; 利用BIOS的0x10中断子功能0x06实现清屏
; 输入:AH 功能号=0x06 AL = 上卷的行数(如果为0,表示全部) BH=上卷行属性
;      (CL,CH) = 窗口左上角的(X,Y)位置, (DL,DH) = 窗口右下角的(X,Y)位置
mov ax,0x600
mov bx,0x700
mov cx,0			; 左上角(0,0)
mov dx,0x184f			; 右下角(80,25)VGA的文本模式,供25行,80int 0x10
; 获取光标位置
mov ah,3
mov bh,0
int 0x10
; 打印字符串
mov ax,message
mov bp,ax
mov cx,5
mov ax,0x1301
mov bx,0x2
int 0x10
; $表示$所在行的地址,即jmp的地址,表示死循环
jmp $

message db  "1 MBR"
times 510-($-$$) db 0		; 除了 代码,其余填充为0
db 0x55,0xaa			; 引导程序的有效标志,供BIOS识别
4.2 编译

编译:nasm -o mbr.bin mbr.S
(安装nasm汇编器: sudo apt-get install nasm(Ubuntu14.04 LTS环境下))

4.3 制作硬盘映像文件并写入

制作硬盘映像文件:
bin/bximage -hd -mode=“flat” -size=60 -q hd60M.img
写入硬盘映像文件
dd if=mbr.bin of=hd60M.img bs=512 count=1 conv=notrunc
(dd: 复制一个文件,根据操作数进行转换和格式化
if: input file,输入文件。故of:output file
bs=512: 写入磁盘是按块大小操作的,这里指定块大小为512字节
count=1: 写入块的数量
conv=notrunc: 这句话建议在追加数据时,conv 最好用 notrunc 方式,也就是不打断文件))

4.4 bochs的配置文件

bochsrc.disk 配置文件如下:

megs: 32

romimage: file=/home/zmk/Documents/bochs/share/bochs/BIOS-bochs-latest
vgaromimage: file=/home/zmk/Documents/bochs/share/bochs/VGABIOS-lgpl-lates

boot: disk
log: bochs.out

mouse: enabled=0
keyboard_mapping: enabled=1,map=/home/zmk/Documents/bochs/share/bochs/keym

ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="hd60M.img", mode=flat, cylinders=121,heads=1

备注:bochs的配置文件实际是设定仿真的PC的硬件环境(如指定启动盘(软盘、硬件或cdrom等)、键盘、鼠标设置、内存大小、日志等)

4.5 运行与结果

bochs在Ubuntu14.04 LTS下的源码安装(bochs-2.6.2-tar.gz)
1)下载源码后解压(tar zxvf xxxxx), 下载路径:https://sourceforge.net/projects/bochs/files/bochs/
2)进入解压后的目录,configure make make install 三部曲走起
./configure
–prefix=/your_path/bochs \ // yourt_path 是你的安装路径
–enable-debugger \ // 打开bochs自己的调试器
–enable-disasm \ // 使bochs支持反汇编
–enable-iodebug \ // 启用io接口调试器
–enable-x86-debugger \ // 支持x86 调试器
–with-x \ // 使用 x windows
–with-x11 \ // 使用x11 图形用户接口,注意每行‘\’结尾前有空格

再 make之前修改Makefile文件,如下 Makefile修改
再运行make,如没有问题最后执行:make install 即可
在bochs的安装目录下运行:bin/bochs (备注:我的配置文件bochsrc.disk在bochs目录下,查找bochs安装目录可用:which bochs)
mbr的运行
开始仿真
continue
运行结果:
mbr引导程序的运行结果

5. 总结

在PC上电后,CS:IP的强制初始化把控制权交给了BIOS后,BIOS执行自检、硬件初始化、设置中断向量等
为加载内核做好充分的准备,最后,BIOS从启动设备把MBR加载到0x7c00后功成身退,MBR的首条远跳转指令跳到
引导程序处,若是硬盘加载OS,还需根据MBR中的分区表的表项找到活动记录标志位0x80的主分区/子拓展分区对应的
OBR,OBR的远眺转到内核加载器(操作系统引导程序)开始加载OS。

参考资料:
《Linux内核完全剖析——基于0.12版本》第17章
《操作系统真象还原》第1、2章

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值