BIOS
BIOS- Base Input&Output System
- - BIOS 是计算机上电后第一个运行的程序
- - BIOS 首先检测硬件状态,检测通过后立即进行硬件初始化
- - BIOS 会在内存中建立中断向量表( 提供硬件访问的方法,比如int 10h中断向量是BIOS提供的中断例程)
- - BIOS 最后将控制权交由主引导程序执行
注意:BIOS 不是软件( Software ) , 而是固件( Firmware ) !
固件是固化于硬件中的程序 ,在硬件出厂前已经烧写固定。
系统启动流程:
BIOS运行机制
- BIOS 存储于 ROM(只读内存,硬盘) 中 , 地址映射为 0xF0000 - 0xFFFFF ( 实地址 )
- BIOS的入口地址为 : 0xFFFF0
- 硬件电路的特殊设计使得 :开机后 , CPU 从 0xFFFF0 处开始执行
BIOS不被谁加载执行,通过硬件设计后,CPU直接从BIOS入口地址开始执行
BIOS 最后的使命
- 按照用户设置扫描各个存储介质( 光驱,软驱,u盘,等 )
- 发现主引导区后 , 将主引导区中的主引导程序载入内存
- 主引导程序被加载到内存中的入口地址为 0x7c00
- 将控制权交由主引程序执行 (
jmp 0x7c00
)
BIOS 如何在存储介质中寻找主引导区(MBR)?
主引导区( MBR : Master Boot Record )
- 位置 : 位于存储介质的最开始位置处,大小为 512 字节
- 特点 : 前 512 字节的最后 2 个有效字节为 0x55aa
- 数据 : 0x55aa 之前的数据被视为主引导程序
两个问题:
每一个存储介质的前512个字节是否一定是主引导区?
不一定,只有最后 2 个有效字节为 0x55aa会被视为主引导区是不是主引导区存储的数据一定是合法的程序呢?
不一定,可能加载到内存后执行出错
小结
- BIOS 是计算机上电后第一运行的程序
- BIOS进行必要的初始化 ,并加载运行主引导程序
- 主引导程序位于存储介质的最开始512 字节处
- 主引导程序负责后续初始化 , 并加载运行操作系统内核
FYOS(实现一个最简单的OS)
主引导程序是软件还是固件? 如果是软件 ,那么由谁开发? 如何开发?
一个实验:
- - 编写一个主引导程序( 汇编语言 )
- - 可独立运行于 x86 架构的主机( 无操作系统 )
- - 运行后在屏幕上打印”Hello,FYOS! “
实现思路
- 将关键寄存器的值设置为0 ( mov ax,0 )
- 定义需要打印的数据( db “Hello,FYOS!” )
- 打印预定义好的字符数据(借助BIOS int 0x10)
一些必备汇编指令:
- mov : 赋值操作,将右操作数赋值给左操作数
mov ax,0 ;持 0 赋值给 ax 寄存器 - int : 触发中断
int 0x10 :触发 0x10 中断 , 对屏幕操作 - hit : 停止运行 , CPU 进入暂停状态 , 不执行任何操作
hit :使程序进入睡眠状态 - 汇编中地址的访问方式 : 段地址 : 段内偏移地址
mov byte [0xb800:0x01], 0x07; 意思是0xb800:0x01 0xb8000 + 0x01
- 标签
用于标识后续指令的地址( 可等同为 C 语言中的标签 ) $ vs $$
$
表示当前指令行地址,$$
表示当前汇编段起始地址
1 | org 0x7c00 ;ORG,其作用是告诉汇编程序,在开始执行的时候,将某段机器语言装载到内存中的哪个地址。 |
那么如何验证编写的主引导程序?
解决方案设计
- 将汇编源码编译为二进制机器码( nasm )
- 创建虚拟盘( bximage )
- 将二进制代码写入虚拟盘起始位置即第0个扇区处( dd )
- 在虚拟机中将虚拟盘作为启动盘执行( vmware )
1 | nasm boot.asm -o boot.bin ;编译汇编代码变为二进制代码 |
首先我在虚拟机里自建了一个空的虚拟机,并且把软盘复制到自建操作系统的目录下面
打开自己创建的操作系统,打印出了传说中的hello world,质的飞跃
ORG指令
- 首先,无论你的汇编代码头部加不加一句
org 0x 7c00
指令,你的主引导扇区代码都会被BIOS自动加载到内存的0x0000:7C00
处,自动加载这是焊死在硬件上面的规范,说白了,自动加载与org
指令半点关系都没有; - 其次,是不是主引导扇区代码,看的是你扇区最后两个字节是不是
0x55aa
,也和org
指令没有半点关系; - 然后,有些主引导扇区汇编代码不加org指令也可以正常运行,就是因为那些代码里面没有涉及到 标号label 相关的代码,不需要对 标号label 进行绝对地址的计算,然而我的上述主引导区代码使用了标号label,因此主引导区代码首句需要
org 0x7c00
[举例]下面这就是没有使用label也没有使用org指令的主引导扇区代码
https://www.jianshu.com/p/207aaf0f986b
- 接着,标号是标号,标号不是寄存器,标号是程序员可以自由任意随便取名字的,取的那个名字只是帮助你阅读代码用的,在代码经过汇编编译器之后标号会被解读成一个具体的数值,
org
影响的就是最后算标号的那个具体数值; - 最后,这个标号代码的那个具体的数值是什么?
是一个offset
,即当前标号所在代码行距离代码开始处的字节数
为什么需要 org 指令?
- 假设,你的标号距离你的代码开始处是
八个字节
,那个标号的值原本应该就是0x08
的,这个原本可以是不写org指令
,也可以是写个org 0x0000
,反正无论哪种这时标号会被解读成数值0x00000008
; - 由于你是主引导扇区的代码,你的代码被自动加载到内存
0x0000:7C00
处了,代码被加载到内存,实际上是说机器码被加载到内存,也就说是说无论你的代码被加载到0x0000:7C00
还是被加载到0x1234:5678
这里, 数值0x00000008
还是数值0x00000008
; - 那么问题就来了?
数值0x00000008
是一个绝对地址,你现在代码全部在0x0000:7C00
,你真正想要找的标号是被放到了从0x0000:7C00
开始后的八个字节的地方,它应该是0x00007C000+0x00000008 = 0x00007C08
而不是0x00000008
- 那么如何解决?
[1] 要么直接修改标号的绝对地址,在使用标号赋值的时候,
强制加上0x7c00
,比如mov ax,bootmsg
写成mov ax,bootmsg+0x7C00
;
[2] 要么,在代码开头使用org 0x7C00
,代码还是写mov ax ,bootmsg
但是它会悄悄地把bootmsg解读成bootmsg+0x7C00
转载于https://www.jianshu.com/p/dbbbc714f942
调试环境的搭建
Bochs( 另一款优秀的虚拟机软件)
- 专业模拟 X86 架构的虚拟机
- 开源且局度可移植 , 由C++ 编写完成
- 支持操作系统开发过程中的断点调试
- 通过简单配置就能够运行绝大数主流的操作系统
支持调试功能的 Bochs 版本
- 下载源码 : https://sourceforge.net/projects/bochs/files/
- 解压缩 bochs-2.x.x.tar.gz 今 bochs-2.x.x
- 进入源码目录 : cd bochs-2.x.x
./configure -enable-debugger -enable-disasm
- make
- sudo make install
配置bochs.txt
启动 bochs 虚拟机
- - 显示方式: bochs-f bochsrc_file
- - 隐式方式 : bochs
boch调试
1 | <bochs:1> break 0x7c00 |
可以看到执行到我们上述编写的汇编的第一条语句
1 | (0) Breakpoint 1, 0x0000000000007c00 in ?? () |
8cc8
是编译mov ax, cs
后得到的16进制数
小结
- Bochs 是一款专业模拟 x86 架构的虚拟机
- 从 源 码 安 装 Bochs 可以获得调试功能的支持
- Bochs 的启动配置文件是正确运行关键
- Bochs 支持断点调试 , 其调试命令与 GDB 类似