【操作系统真象还原】第2章:编写 MBR 主引导记录,让我们开始掌权

目录

1. 计算机的启动过程

2. 软件接力第一棒,BIOS

2.1 实模式下的 1MB 内存布局

 2.2 BIOS 是如何苏醒的

3. 让 MBR 先飞一会儿

3.1 神奇好用的$和$$,令人迷惑的 section

3.2 NASM简单用法

3.3 请下一位选手 MBR 同学做准备


1. 计算机的启动过程

CPU 的硬件电路被设计成只能运行处于内存中的程序,这是硬件基因的问题。

程序载入内存分为两部分:程序被加载器(软件或硬件)加载到内存某个区域;CPU 的 cs:ip 寄存器被指向这个程序的起始地址。

操作系统在加载程序时,是需要某个加载器(本质上就是一堆函数组成的模块)来将用户程序存储到内存中的。

从按下主机上的 power 键后,第一个运行的软件是 BIOS。

本节🍊:不要因为未知的东西而感到畏惧!!!

2. 软件接力第一棒,BIOS

BIOS 全称叫 Base Input & Output System,即基本输入输出系统。

BIOS 的主要工作是检测、初始化硬件,硬件自己提供了一些初始化的功能调用,BIOS 直接调用就好了。BIOS 还做了一件伟大的事情,建立了中断向量表,这样就可以通过 “int中断号” 来实现相关的硬件调用。

本节🍊:人们给任何事物起名字,肯定都不是乱起的,必然是根据该事物的特点,通过总结,精练出一些文字来标识此事物,这个便是对一般事物取名的方法。通过名字,就能够反应出该事物的特性。

2.1 实模式下的 1MB 内存布局

内存空间(地址总线宽度—计算机寻址范围)需要分配给几个部分:ROM + 一些外设 +物理内存(也就是内存条 or DRAM)。

本节🍊:我们学习新的知识,很多时候都是建立在原有的知识上,用原有的知识帮助学习新的知识;想象、思考,帮助理解,对于一个新知识的掌握,能够自圆其说,就够了。

2.2 BIOS 是如何苏醒的

只读存储器 ROM:这种存储介质是用来存储一成不变的数据的。

BIOS 所在的 ROM 被映射在低端 lMB 内存的顶部,即地址0xF0000~0xFFFFF处,只要访问此处的地址便是访问了 BIOS,这个映射是由硬件完成的。BIOS 本身是个程序,程序要执行,就要有个入口地址才行,此入口地址便是 0xFFFF0

在开机的一瞬间,也就是接电的一瞬间,CPU 的 cs:ip 寄存器被强制初始化为 0xF000:0xFFF0。由于开机的时候处于实模式,在实模式下的段基址要乘以 16 ,也就是左移一位,于是 0xF000:0xFFF0 的等效地址将是 0xFFFF0。

BIOS 是在实模式下运行的,而实模式只能访问 1MB 空间 (20位地址线, 2的20次方是 1MB)。——应该是为了兼容吧~

BIOS 入口处的代码是个跳转指令,用来跳转到真正的 BIOS 代码处,f000: e05b 也就是 0xfe05b 处才是 BIOS 代码真正开始的地方。

接下来 BIOS 便马不停蹄地检测内存,显卡等外设信息,当检测通过并初始化好硬件后,开始在内 存中 0x000 0x3FF 处建立数据结构、中断向量表 IVT 并填写中断例程。

BIOS 最后一项工作是校验启动盘中位于0盘0道1扇区的内容,(磁盘上最开始的那个扇区),如果此扇区末尾的两个字节分别是魔数 0x55,0xaa,BIOS 便认为此扇区中确实存在可执行的程序,(此程序便是久闻大名的主引导记录 MBR),便加载到物理地址 0x7c00(0:0x7c00),随后跳转到此地址,继续执行。

加载 MBR 的位置取决于操作系统本身所占内存大小和当前的内存布局。

通常,MBR 的任务是加载某个程序(这个程序一般是内核加载器,很少有直接加载内核的)到指定位置,并将控制权交给它,所谓的交控制权就是 jmp 过去而己。

BIOS总结:在实模式下工作

  1. BIOS 所在地址空间:0xF0000~0xFFFFF(低端 lMB 内存的顶部);
  2. 开机接电,cs:ip 寄存器被强制初始化为 0xF000:0xFFF0,即 BIOS 的入口地址 0xFFFF0;
  3. 入口地址处的跳转指令跳转至 0xf000: 0xe05b 的 BIOS 代码处,开始执行初始化等操作;
  4. 代码最后的工作是检查磁盘上最开始的那个扇区,若末尾两个字节为0x55,0xaa,则将该扇区512个字节的 MBR 程序加载到 0x7c00,cs从0xf000变为0。

至此,BIOS 的工作就结束了🎉🎉🎉

疑问???

内存中,是程序就要用到栈(🤔️)

3. 让 MBR 先飞一会儿

主引导记录 MBR 的任务是加载内核加载器到指定位置,并将控制权交给它。

MBR 位于磁盘上最开始的那个扇区,其大小必须是 512 字节—这是为了保证 0x55, 0xaa 这两个 魔数恰好出现在该扇区的最后两个字节处。

3.1 神奇好用的$和$$,令人迷惑的 section

伪指令是相对于 CPU 可识别的指令来说的,它(伪指令)只是编译器定义的,CPU 中并不存在这个指令,伪指令是编译器为了开发人员写代码方便而提供的一些符号,这些符号在编译时,会由编译器转换成 CPU 可识别的东西,如指令或地址等。

汇编语言中的标号是程序员“显式地”写在明处的,这个标号会被 nasm 认为是一个地址。

$属于“隐式地”藏在本行代码前的标号,也就是编译器(遇到的话)给当前行安排的地址,看不到却又无处不在,每一行都有。

$$指代本 section 的起始地址,此地址同样是编译器给安排的。

section 是给开发人员逻辑上规划代码用的,只起到思路清晰的作用,最终还是在编译阶段由 nasm 在物理上的规划说了算。

3.2 NASM简单用法

nasm 的基本用法:

nasm -f <format><filename> [-o <output> ]

-o 就是指定输出可执行文件的名称

-f 用来指定输出文件的格式

bin 是指纯二进制格式:纯二进制就是不掺杂其他的东西,直接给 CPU 后就能用,也就是可执行文件中什么样,内存中就什么样。

elf 或 pe 格式的二进制可执行文件,那里面有好多和指令无关的东西,里面掺杂了程序的内存布局、位置等信息,这是给操作系统中的程序加载器用的,是属于操作系统规划的范畴了。

3.3 请下一位选手 MBR 同学做准备

;主引导程序 MBR
;————————————————————————————————————————————————————————————
SECTION MBR vstart=0x7c00
	mov ax,cs    ;此时cs中的值为0x0
	mov ds,ax    ;用cs寄存器的值来初始化其他寄存器的值,
	mov es,ax    ;因为没有从立即数到段寄存器的电路实现,只有通过其他寄存器来中转
	mov ss,ax
	mov fs,ax
	mov sp,0x7c00    ;初始化栈指针,栈是向下发展的,是程序就要用到栈???

;————————————————————————————————————————————————————————————
;清屏,因为在 BIOS 工作中,会有一些输出,如检测硬件的结果信息等。为了让大家看清楚我们在MBR 中的输出字符串,故先把 BIOS 的输出清掉,这里演示的是 BIOS 中断 int 0x10 的用法。
;清屏利用 0x06 号功能,上卷全部行,则可清屏
;INT 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 文本模式中,一行只能容纳 80 个字符,共 25 行
					;下标从0开始,所以 0x18=24,0x4f=79(好像是 列,行 坐标?)
	int 0x10

;————————————————————————————————————————————————————————————
;获取光标位置,光标只是在视觉上告诉我们字符写到哪里了
;get_cursor 获取当前光标位置,在光标位置处打印字符
	mov ah,3	;输入: 3 号子功能是获取光标位置,需要存入 ah 寄存器
				;(发现:一个中断例程好像有多个子功能,功能号要放入ah寄存器中来调用)
	mov bh,0	;bh 寄存器存储的是待获取光标的页号
	int 0x10	;输出: ch =光标开始行, cl =光标结束行
				;	   dh=光标所在行号, dl =光标所在列号

;————————————————————————————————————————————————————————————
;打印字符串,还是用 10h 中断,不过这次调用 13 号子功能打印字符串
	mov ax, message
	mov bp, ax		;es:bp 为串首地址, es 此时同 cs 一致,开头时已经为 sreg 初始化
					;bp寄存器???
					;(note:立即数需要通过通用寄存器存入段寄存器)

;光标位置要用到 dx 寄存器中内容, cx 中的光标位置可忽略
	mov cx,12		;cx 为串长度,不包括结束符0的字符个数
	mov ax,0x1301	;子功能号13显示字符及属性,要存入ah寄存器
					;al 中设置写字符方式,al=01:显示字符串,光标跟随移动
	mov bx,0x2		;bh 存储要显示的页号,此处是第0页
					;bl 中是字符属性,属性黑底绿字(bl = 02h)
	int 0x10		;执行 BIOS 0x10 号中断

;————————————————————————————————————————————————————————————
	jmp $	;使程序悬停在此


	message db "Hello World!"
	times 510-($-$$) db 0
	db 0x55,0xaa


编译:nasm -o mbr.bin mbr.S (汇编语言是 .S格式?)

写入磁盘:

Linux命令 dd 是用于磁盘操作的命令

一些参数介绍:🍊够用就行了,需要时再学(man dd 查看帮助文件)

if = FILE: 此项是指定要读取的文件

of = FILE: 此项是指定把数据输出到哪个文件

bs = BYTES: 此项指定块的大小, dd 是以块为单位来进行 IO 操作的,得告诉人家块是多大字节,此项是统计配置了输入块大小 ibs 和输出块大小 obs,这两个也可以单独配置。

count = BLOCKS: 此项是指定拷贝的块数

seek = BLOCKS: 此项是指定当我们把块输出到文件时想要跳过多少个块

conv = CONVS: 此项是指定如何转换文件

append append mode (makes sense only for output; conv=notrunc suggested):

建议在追加数据时, conv 最好用 notrunc 方式,也就是不打断文件。


dd if=/your__path/mbr.bin of=/your__path/bochs/hd60.img bs=512 count=1 conv=notrunc

输入文件是刚刚编译出来的 mbr.bin,输出是我们虚拟出来的硬盘 hd60.img,块大小指定为 512 字节,只操作1块,即总共 1*512=512 字节,由于想写入第0块,所以没用seek指定跳过的块数。

启动bochs虚拟机软件:在bochs安装目录下,bin/bochs -f bochsrc.disk(启动 bochs 的时候,用 -f 来指定其配置文件)

奇怪,光标咋不在左上角🤔️,不过还是🎉🎉🎉啦~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值