从0开始的操作系统手搓教程 2:从MBR开始我们的征程

让我们从MBR开始

目录

让我们从MBR开始

前言

等等,我还有些迷糊...

下一个问题,啥是BIOS

什么是MBR

笔者翻阅的参考


前言

现在,我们终于可以开始严肃的展开我们的宏伟进程,那就是实现一个操作系统。但我们必须知道,即使是一个玩具级别的操作系统,其工程量也是艰巨的,我们需要一步一步慢慢来。所以现在,就让我们从MBR开始我们的征程

笔者会简单的介绍BIOS和MBR的基本概念,完成一个玩具MBR的书写,并完成对bochs和硬盘制作的简单教程,当然,不会深入,这是我们之后要做的事情!

等等,我还有些迷糊...

是的,对于初学者而言,上来我就抛出的MBR的概念,属于是又一些为难人。这里需要说明的是,我们的计算机一上电的时候,其标志着程序运行流状态的两个寄存器——当然我们在讨论的是x86的架构上——也就是我们的CS寄存器(Code Segment)和IP寄存器(Instruction Pointer,也就是程序指针寄存器)就被赋予了一个这样的值,那就是:

寄存器取值
CS0xF000
IP0xFFF0

当然,一些教材会告诉你,CS和IP会被初始化位0xFFFF:0x0000,这是一种说法。笔者上面给出的是参考了我们Intel IA32手册的结论,截图如下:

然而,另一些教材,比如说《Linux内核设计的艺术》此书认为是0xFFFF:0x0000,关于此,笔者认为是硬件设置的问题,但是不管如何,我们最后得到的地址,按照实模式16位地址组合的办法,得到的依然是0xFFFF0这个地址(实模式的段地址左移4位加上偏移量),这个地址非常的关键,笔者在这里提前截了图,请看:

在这里,我们的Bochs模拟器依照的是Intel IA32指定的方式进行了初始化,所以,笔者决定将CS:IP为0xF000:0xFFF0作为一个结论放到这里!

笔者之前也有阅读的是AMD的架构手册,这一点上为了兼容性没有做更改,好奇的朋友可以一睹芳容!

AMD Profile: Vol2: System Programming at 14.1.3 : Processor Initialization State

所以现在,我们就开始好奇,既然我们CPU的上电已经确定是0xFFFF0了,那么显然,看官也就知道,我们的CPU就马上会去执行这个物理地址空间上的代码。所以他是什么呢?答案是这个指令(眼尖的朋友已经看到了我们的Bochs给出了指令)

jmpf 0xf000:e05b    ; for real mode, its 0xfe05b

一个远跳指令!对于我们的这种最简单的系统,我们的bochs支持的是使用BIOS(Basic Input/Output System)作为我们启动软件

下一个问题,啥是BIOS

很好!我们马上引导到一个新的问题,什么是BIOS呢?我们知道的是,CPU接受到了RESET脉冲准备重启的时候,他会初始化一部分硬件(上面我给出了,寄存器的值),但是远远不足以驱动我们来加载操作系统这些更加高级的硬件。所以,BIOS这个基本输入输出系统,就充当了我们硬件架构层和软件应用层(说真的跟我们习惯的应用层差远了)的接力棒,这个接力棒就会进行一部分基础的初始化,比如说对我们显卡最基本的模式进行检查和启动,对我们的硬盘进行检查(咱们的操作系统会启动在硬盘上,必须检查我们的物理环境是合格的操作系统才能正确的有容身之处工作,不是嘛?)

非常感谢《操作系统还原真相》这本书,我不必去幸幸苦苦翻阅BIOS的资料了。我们现在的IA32架构的虚拟机上,还处于16位的实模式,地址访问还是20位(关于为什么是20位,到这个地方还不理解的话,可以查看笔者在bonus文件夹下的部分文章作为一个额外的解答)。在这1MB大小(2^{20}b = 1 MB)的实模式的内存海洋中,已经被约定为如下的内存排布,我们的BIOS作为一定的初始化(基本硬件检查和硬件中断的默认注册)之后,就会执行一个跳转代码(值得注意的是Bochs是陷入一个跳转的中断后直接跳到MBR引导)。

起始结束大小用途
FFFF0FFFFF16BBIOS入口地址,此地址也属于BIOS代码,同样属于顶部的640KB字节。只是强调其入口地址才单独贴出来。此处16字节的内容是跳转指令 jmp f000:e05b
F0000FFFEF64KB-16B系统BIOS范围是 F0000~FFFFF 共 64KB,为说明入口地址,将最上面的字节从此处去掉了,所以此处终止地址是 0XFFFFEF。
C8000EFFFF160KB映射硬件适配器的 ROM 或内存映射式 I/O。
C0000C7FFF32KB显示适配器 BIOS。
B8000BFFFF32KB用于文本模式显示适配器。
B0000B7FFF32KB用于黑白显示适配器。
A0000AFFFF64KB用于彩色显示适配器。
9FC009FFFF1KBEBDA(Extended BIOS Data Area)扩展 BIOS 数据区。
7E009FBFF622080B (约608KB)可用区域。
7C007DFF512BMBR 被 BIOS 加载到此处,共 512 字节。
5007BFF30464B (约30KB)可用区域。
4004FF256BBIOS Data Area(BIOS 数据区)。
0003FF1KBInterrupt Vector Table(中断向量表)。

BIOS做好了一部分必备的初始化(一个好理解的办法就是想到我们的STM32的初始化文件,都会安排好一部分必要的中断,都会进行自己管辖范围内的内存的初始化等等的操作)之后,就会开始考虑自己的后事——发现我们的主引导记录(也就是我们的主角MBR),并且把程序流转接到那里。办法就是检查我们的引导扇区(可以理解为我们给BIOS提供的一个保障:放心的交给我这个上面的硬盘,上面有你的接班人MBR),办法?那自然是提供一个魔数(身份),看到一个0x55aa那就是一个MBR,直接把这个扇区放到我们的物理地址0x7c00处(需要理解的是:一个硬盘的扇区是512B大小,也就说0x200个字节,所以是0x7C00 ~ 0x7E00!)

不管怎么说,BIOS就是一个负责接力的软件,在那之后,他便把程序流引导到我们的MBR,也就是接下来的主角。Master Boot Record上去了。

什么是MBR

很好,从这里开始,就是我们准备动手的地方了。现在程序流来到了这个地方,那么,问题就在于,我们如何继续往下走呢?是的,这就是我们的工作了,我们要做的就是进一步的去编写我们MBR内部的引导程序,让他进行下一步的操作,比如说,马上我们编写结束MBR后,就会转身去做下一个操作,那就是加载操作系统,但是现在先不着急,我们可以现在MBR内部玩玩。

上面提到了,BIOS认为一个扇区的最后两个字节是0x55aa的时候,他就是一个内部存在引导代码的引导扇区,我们的做法就是简单的将我们的这个扇区放上这个0x55aa就完事了。

所以到这里,我们的做法就很简单了

任务:编写一个简单的MBR引导程序,大小必须是正好的一个扇区,需要做的是将我们的汇编程序起手编译到我们的0x7c00这个地址,然后将这个程序的最后两个字节安排上0x55aa

现在请看笔者的代码

; virtual start, the mbr code place should be start as 0x7c00
; this can be referenced from BIOS, see the BIOS doc for details
%define MBR_VSTART          0x7c00
; jmp to itself
%define STOP                jmp $
%define PLACE_MBR_MAGIC     db 0x55, 0xaa
​
; sections tells the program parts functionalities
; the name MBR is not the must, it;s just inform us
; that the code is a mbr code
; set the code offset address as the 0x7c00
; then the running flow will jumps here
SECTION MBR vstart=MBR_VSTART
    ; Load Segments
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov fs, ax
    mov sp, MBR_VSTART
    mov ax, 0xb800
    mov gs, ax
​
; clear the screen by using 0x06
​
    mov ax, 0x0600
    mov bx, 0x0700
    xor cx, cx
    mov dx, 0x184f   ; 25 rows, 80 cols
    int 0x10
​
; set the cursor to the (0, 0)
; we are ready to display some infomations
    mov ah, 0x02     ; 0x02 is the funtionalities of BIOS
                    ; who helps set the cursor
    mov bh, 0        ; 页码0
    mov dh, 0        ; 行号0 (第一行)
    mov dl, 0        ; 列号0 (第一列)
    int 0x10         ; 调用中断
​
; get the cursor positions
; this is because we will ready to print strings, the bh should be the page
; index, so we fetch it using funtional index 0x02
    mov ah, 0x02
    mov bh, 0
    int 0x10
    
    ; at here, we fetch the bh as the page index
    ; gs is the register that will be used for BIOS
    ; for display, meaning that we need to write messages to there
    mov di, 0x00        ; we set as the start
​
    ; register the string 
    mov si, welcome     ; pass the soruce index to si
    
; start our loop to place the strings
store_loop:
    ; lodsb promised to load a char from si, and ++si
    lodsb               ; 加载一个字符到 al,并更新 si
    ; once the al is 0, we convinced its the end!
    or al, al           
    jz done             ; and we quit the issue 
​
    ; still the char we need to print, load to the gs
    mov [gs:di], al     ; 
    inc di              ; update the di
​
    ; 存储 0xA4 到 [gs:di]
    mov byte [gs:di], 0xA4  ; 0xA4 is the char style, see doc for the ref
    inc di              ; update the di, ready for the next writing sessions
​
    jmp store_loop      ; 
​
done:
    STOP                ; stop, stuck the cs:ip
​
; the string we display
welcome db "Hello, Charlie's OS", 0
; the string index we put
welcome_len equ $ - welcome
; replace all 0 to the rest of place!
times 510 - ($ - $$) db 0
; and place the final magic, done!
PLACE_MBR_MAGIC

这就是代码,一般的,我们需要上电看看现象。

笔者在setup的时候就强调过,你需要nasm来完成这个文件的编译,办法如下:

nasm -o mbr.bin mbr.S

在这里,我们会把mbr.S文件按照nasm的编译规则编译成可执行的二进制文件(看到.bin文件后缀了嘛,这个就是代表本文件是一个可以执行的二进制文件的含义)

之后,按照约定,我们会把这个文件刻录到我们的硬盘当中,我们的操作系统将会从硬盘启动!

所以现在,我们就是要制作一个硬盘帮助我们模拟一个虚拟机操作系统启动的必备硬件了。这里给出一个文档,你需要按照这个做出配置(bximage的使用教程)

无论你是使用包管理器,还是使用自己编译的方式,我们最后的安装文件夹下都会存在一个叫做bximage的可执行文件,他是一个简单的迷你的创建软盘和硬盘的小软件。

========================================================================
                                bximage
  Disk Image Creation / Conversion / Resize and Commit Tool for Bochs
                                  $Id$
========================================================================
​
1. Create new floppy or hard disk image
2. Convert hard disk image to other format (mode)
3. Resize hard disk image
4. Commit 'undoable' redolog to base image
5. Disk image info
​
0. Quit
​
Please choose one [0] 

我们需要制作的是一个硬件盘,所以选1,回车

Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] 

默认是硬盘,回车

What kind of image should I create?
Please type flat, sparse, growing, vpc or vmware4. [flat] 

默认的,回车

Choose the size of hard disk sectors.
Please type 512, 1024 or 4096. [512] 

我们选择512。也就是默认的(为什么是512,想想我怎么说MBR的特征的)

Enter the hard disk size in megabytes, between 10 and 8257535
[10] 60 

我们创建一个60M大小的磁盘,这样的话够用到我们操作系统手搓教程的结尾。

What should be the name of the image?
[c.img] boot.img 

然后就好了,当然,名称自己任意!

当然他还有其他的功能,比如说软硬盘转化,重新设置硬盘大小等等功能,我们这里的教程都不会用到,感兴趣的朋友请自行查阅!

制作好了硬盘,我们还需要将我们的二进制文件烧录到硬盘当中去。

dd if=mbr.bin of=boot.img bs=512 count=1 conv=notrunc

dd是一个Linux下的一个方便的二进制工具,关于dd的完全使用,笔者认为你需要自己学会查阅文档,这里给出一个范例:

dd --help
Usage: dd [OPERAND]...
  or:  dd OPTION
Copy a file, converting and formatting according to the operands.
... # 之后的省略

笔者建议你先查阅文档,看看参数的含义都是什么意思后,我在继续说。

很简单,就是按照不截断的方式,将我们的mbr二进制文件烧录到一个叫做boot.img的文件当中,烧录的大小是1块512字节区块!

>> dd if=mbr.bin of=boot.img bs=512 count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 7.7208e-05 s, 6.6 MB/s

之后我们需要正确的配置我们的boshsrc,boshsrc存放着我们对bochs启动的配置文件!请看文档:

配置boshsrc是一个比较枯燥的事情,我们先来看一个笔者自己查阅资料(实际上是CV的《操作系统还原真相》此书的配置做了适当的修改)

# My Configure in Bochs
​
# RAM size available
megs:               32
​
​
# BIOS and VGA BIOS
# modify the romimage and vgaromimage to the place in your operating system
romimage:           file=/usr/local/share/bochs/BIOS-bochs-latest
vgaromimage:        file=/usr/local/share/bochs/VGABIOS-lgpl-latest
​
​
# USE WHAT
boot:               disk
log:                bochs.log.out
​
​
# Configure More IO Device
mouse:              enabled=0
# set the keyboard map to the place
keyboard:           keymap=/usr/local/share/bochs/keymaps/x11-pc-us.map
​
# disk related
ata0:               enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
​
# Modify the boot sections, path should be the image for the boot!
ata0-master:        type=disk, path="boot.img", mode=flat

当然,我们的bochs不支持使用gdb进行调试,我们直接使用的是内置的调试器进行的调试。

现在就是激动人心的启动时刻了!

bochs -f bochsrc

这句话将会指引我们的bochs读取我们在上面的文档中书写的bochs启动配置。

现在我们就会来到这个地方:

哈哈,现在可以验证我之前所说的内容了,关于bochs调试的基本办法等内容我会在之后的博客中一一铺开。

bochs现在再等待我们输入指令,我们输入c并敲下回车,得到了一个我们振奋的结果。

一个闪烁的Hello, Charlie's OS!,你可以换成任何你喜欢的字符串打印!恭喜你!你已经迈出了手搓最基本的操作系统的第一步!

想要结束我们的模拟,嗯下Ctrl + C,输入q回车后,就会退出我们的模拟了!

下一篇

从0开始的操作系统手搓教程 3:继续我们的MBR事业-CSDN博客文章浏览阅读1.5k次,点赞49次,收藏10次。笔者这里将会进一步详细的讨论一些概念,当然,如果你之前已经编写过操作系统的引导文件过,自然可以跳过这个章节。笔者默认您会NASM和基本的Intel汇编,当然,如果你不是的话,还请看看笔者的唠叨。笔者这一章节,将会复习有关我们MBR编写相关的汇编语言部分,以及关于NASM的简单介绍,到最后,我们会将我们学习到的知识以更好的完善我们的MBR。这样,我们才算是真正的吃透了MBR的编写,才好扎实我们的Loader编写的任务。 https://blog.csdn.net/charlie114514191/article/details/145620354

代码

GitHub - Charliechen114514/CCOperateSystem: A Tutorial Trying teach you how to make an os in modern tools with gcc, nasm and bochs!A Tutorial Trying teach you how to make an os in modern tools with gcc, nasm and bochs! - Charliechen114514/CCOperateSystemhttps://github.com/Charliechen114514/CCOperateSystem

笔者翻阅的参考

  1. IA32手册:Intel® 64 and IA-32 Architectures Software Developer Manuals,笔者下载的是1~4卷,当然,对汇编足够熟悉的朋友不妨直接下载第三卷,直接阅读系统编程

  2. 《操作系统还原真相》作者: 郑钢操作系统真象还原 (豆瓣)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值