文章目录
写在前面:自制操作系统Gos 第二章第二篇:主要内容是CPU中权力的移交,如何控制BIOS和MBR
BIOS
有过操作系统基础的同学都知道,当操作系统通电之后,会先加载BIOS,之后BIOS将会加载操作系统。说起来这个过程好像很简单一样,但是其实其中有很多细节是我们并不了解的,而这些细节正式掌握操作系统的屠龙技。
什么是BIOS
BIOS的全程是Base Input Output System,即基本输入输入系统。可以看到有这么几个关键词哈:基本、输入输出、系统。
不禁有这么几个疑问了:
- 基本是什么意思?
- 输入输出的又是什么?
我们先来看基本的意思哈。
8086的实模式内存布局 重点!!!
在Intel 8086中总共有20根地址,那么其实CPU可以访问到的地址范围也就是220 = 1M的范围。而这宝贵的1M空间又被划分为以下区域:
起始 | 结束 | 大小 | 用途 |
---|---|---|---|
FFFF0 | FFFFF | 16B |
BIOS的入口地址,此处的内容主要是jmp f000:e05b
|
F0000 | FFFEF | 64KB-16B | BIOS的其他地址 |
C8000 | EFFFF | 160KB | 映射硬件适配器ROM或者内存映射 I/O |
C0000 | C7FFF | 32KB | 显示适配器BIOS |
B8000 | BFFFF | 32KB | 用于文本模式显示适配器 |
B0000 | B7FFF | 32KB | 用于黑白显示适配器 |
A0000 | AFFFF | 64KB | 用于彩色显示适配器 |
9FC00 | 9FFFF | 1KB | EBDA扩展BIOS数据区 |
7E00 | 7DFF | 512B | MBR加载区 |
500 | 4FF | 256B | BIOS数据区 |
000 | 3FF | 1KB | 中断向量表 |
这个表很重要,它相当于我们屠龙技的剑谱,如何屠龙都得依着他来。
我们看一开始的0xFFFF0 ~ 0xFFFFF
这段区域,这64KB内存是ROM,里面存储的就是BIOS代码。BIOS的主要工作就是检测、初始化硬件。这个过程放大来看就是BIOS调用硬件驱动提供的硬件驱动函数。除此之外,BIOS做的另一件事情就是初始化中断向量表,这样就可以通过 int 中断号
这个汇编指令来调用中断函数了。
而为什么称之为基本输入输出呢,因为BIOS是工作在实模式下面的,其不用做所有工作,只用完成基本的初始化工作就好了,真正广阔的星辰大海在保护模式呢!
BIOS启动
从我们整篇文章的开头我们知道,BIOS可以说是整个计算机第一个启动的软件了,那么它又是被谁启动的呢?是自举么?
其实是硬件啦。实模式下x86的CPU通过段基址寄存器cs
和段偏移寄存器ip
来实现对内存的访问。在开机的一瞬间,CPU的cs:ip
便被初始化为0xF000:0xFFF0
,此地址便是BIOS的入口地址。
注:
在x86模式下面有个约定,为了让16位的寄存器能够访问20位大小的内存空间,需要段基地址左移4位,那么这里0xF000:0xFFF0
其实等价于0xFFFF0
之后,BIOS执行指令jmp f000:e05b
,跳转到BIOS真正开始执行的地方。之后BIOS就开始检测内存、显卡等外设信息,当检测通过的时候并初始化硬件之后,BIOS会在0x000~0x3FF
处建立中断向量表并填写中断例程。
MBR
当BIOS初始化成功之后,他就完成了他的工作。之后他就会把权力交给MBR。他是我们能够掌控的第一个程序。
什么是MBR
MBR即Main Boot Record,它位于整个硬盘最开始的扇区,即0盘0道1扇区,这扇区便被成为MBR引导扇区。而这个引导扇区的内容如下:
- 446字节的引导程序及参数
- 64字节的分区表
- 2字节的结束标记
0x55
和0xaa
注:
关于权力是怎么交接的,则是BIOS做完工作之后,从磁盘的一开始检测0x55
和0xaa
这两个标记,当检测到了,他就认为这个是继任者MBR。
之后呢,BIOS将这512字节加载到0x7c00
这个位置,然后jmp
过去,这样就完成了权力的交接工作了,开始执行MBR中的代码了。
MBR的内容
终于到硬菜了,我们开始掌控操作系统了,从我们的MBR程序开始。以下代码是程序的内容,看不懂评论区留言。
;主引导程序
SECTION MBR vstart=0x7c00
mov ax,cs ;用cs寄存器去初始化其他寄存器
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00 ;初始化栈指针
; 清屏利用0x06号功能,上卷全部行,进行清屏
; int 0x10 功能号:0x60 功能描述:上卷窗口
; 输入:
; 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)
;VAG文本模式中,一行只能容纳80个字符,总共25行
;下标从0开始,所以0x18=24,0x4f=79
int 0x10 ;系统调用号10
;下面三行汇编代码获取光标位置
; .get_cursor获取当前光标的位置,在光标位置处打印字符
mov ah,3 ;输入:3号的子功能是获取光标位置,需要存入ah寄存器
mov bh,0 ;bh寄存器存储的是待获取光标的页号
int 0x10 ;输出:ch=光标开始行,cl=光标结束行
;dh=光标所在行号,dl=光标所在列号
;打印字符串
;还是10h中断,不过这次调用13号子功能打印字符串
mov ax,message
mov bp,ax ;es:bp 为串的首地址,es此时同cs一致,开头时已经为sreg初始化
;光标位置要用到寄存器dx中内容,cs中的光标位置可以忽略
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 ;扩充成510字节
db 0x55,0xaa
之后,我们调用nasm编译这段汇编代码:
# 编译命令
nasm -o mbr.bin mbr.S
然后使用dd工具将其写入我们之前创建的hd60M.img
这个磁盘的第一个扇区中:
# 生成的可执行文件写入 hd60M.img 这个虚拟硬盘的 0盘 0道 1扇区中
# 记得 of后面换成自己的 bochs地址
sudo dd if=/home/ik/workspace/mbr.bin of=/bochs/bo_tmp/bin/hd60M.img bs=512 count=1 conv=notrunc
之后转到自己安装bochs的地方,执行以下命令运行,我们看一下结果:
参考文献
[1] 深入理解计算机系统
[2] 操作系统真相还原