【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇

一.概述

        有了上一篇文章:【操作系统】调用硬盘并且实现MBR与Loader的过渡——原理篇的理论支持,我们就可以开始代码实操了,接下来我们将优化MBR程序,使其从扇区中读取出loader加载器,并将其存放到内存处,将权限交付给loader加载器。

二.MBR代码优化

        在开始之前我们还需要注意一些细节问题:

  1. 由于MBR程序已经占据了0扇区,所以loader加载器只能放在其他位置,本文就将加载器放在2扇区,如果你想放其他地方也可以,只要保证不被覆盖就行。
  2. 读取出来的加载器应该放在内存的什么位置?答案可以参考之前写过的一篇文章:【操作系统】BIOS开机自检,在内存布局中我们得知了“可用区域”0x500~0x7BFF和0X7E00~0X9FBFF,所以我们只要把加载器放在这两个区间就可以,本文就将加载器放在内存的0x900的位置处。

        万事俱备,我们就可以开始编写代码了:

 1 SECTION MBR vstart=0x7c00
  2         mov ax,cs
  3         mov ds,ax
  4         mov es,ax
  5         mov ss,ax
  6         mov fs,ax
  7         mov sp,0x7c00
  8 
  9         mov ax,0xb800
 10         mov gs,ax
 11 
 12         mov ax,0x600
 13         mov bx,0x700
 14         mov cx,0
 15         mov dx,0x184f
 16 
 17         int 0x10
 18 
 19         mov byte [gs:0x00],'1'
 20         mov byte [gs:0x01],0xA4
 21 
 22         mov byte [gs:0x02],' '
 23         mov byte [gs:0x03],0xA4
 24 
 25         mov byte [gs:0x04],'M'
 26         mov byte [gs:0x05],0xA4
 27 
 28         mov byte [gs:0x06],'B'
 29         mov byte [gs:0x07],0xA4
 30 
 31         mov byte [gs:0x08],'R'
 32         mov byte [gs:0x09],0xA4
 33 
 34         mov eax,LOADER_START_SECTOR
 35         mov bx,LOADER_BASE_ADDR
 36         mov cx,1
 37         call rd_disk_m_16
 38         jmp LOADER_BASE_ADDR
 39 
 40 rd_disk_m_16:
 41         mov esi,eax
 42         mov di,cx
 43 
 44         mov dx,0x1f2
 45         mov al,cl
 46         out dx,al
 47 
 48         mov eax,esi
 49 
 50         mov dx,0x1f3
 51         out dx,al
 52 
 53         mov cl,8
 54         shr eax,cl
 55         mov dx,0x1f4
 56         out dx,al
 57 
 58         shr eax,cl
 59         mov dx,0x1f5
 60         out dx,al
 61 
 62         shr eax,cl
 63         and al,0x0f
 64         or al,0xe0
 65         mov dx,0x1f6
 66         out dx,al
 67 
 68         mov dx,0x1f7
 69         mov al,0x20
 70         out dx,al
 71 
 72 .not_ready:
 73         nop
 74         in al,dx
 75         and al,0x88
 76         cmp al,0x08
 77         jnz .not_ready
 78 
 79         mov ax,di
 80         mov dx,256
 81         mul dx
 82         mov cx,ax
 83 
 84         mov dx,0x1f0
 85 
 86 .go_on_read:
 87         in ax,dx
 88         mov [bx],ax
 89         add bx,2
 90         loop .go_on_read
 91         ret
 92 
 93         times 510-($-$$) db 0
 94         db 0x55,0xaa

三.MBR代码解析

        在第一行的“%include”是我们的配置文件,我们可以将加载器的配置信息填写在里面,目前配置信息就只有两个:

LOADER_BASE_ADDR equ 0x900  //定义loader在内存的位置

LOADER_START_SECTOR equ 0x2  //定义loader在硬盘上的LBA地址

        接下来的第2行~第32行是上一篇文章(【操作系统】优化MBR程序:让MBR调用显存吧)的原始代码,就不在此赘述了。第34行~第35行使用了上述配置文件的信息,是第40行函数rd_disk_m_16的传参,这个函数需要三个参数,用eax\bx\cx三个寄存器来传递参数。

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第1张图片

        寄存器eax中保存了待读取的扇区起始地址,根据第四节的要求,加载器放在了2扇区的位置。寄存器cx中保存了读取扇区的数量,此处要读入1个扇区。寄存器bx保存从硬盘中读取出来的数据放在内存的什么位置,同样按照第四节的要求,将其放在0x900的位置。紧接着开始调用函数rd_disk_m_16,该函数的功能为读取硬盘的指定扇区数据。

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第2张图片

        首先我们将eax和cx的值备份在esi中,因为后面al寄存器需要用到,所以为了避免数据覆盖进行备份操作。

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第3张图片

        按照上一篇文章第三节的调用步骤,我们先来选择通道,往该通道的sector count寄存器中写入代操作的扇区数。根据之前写过的一篇文章:【操作系统】Bochs安装和配置,我们的模拟硬件的配置信息为:

        ata0是Primary通道,所以sector count寄存器是端口0x1F2来调用。Out指令用于往端口写入数据,dx保存端口信息,al保存cx的信息也就是待读取的扇区数。

        使用完al之后,我们回复一下eax的值,让其重新保存待读取的扇区起始地址。

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第4张图片

         本段主要实现上一篇文章第三节的第二和第三个步骤,将LBA地址分别写入到三个LBA寄存器中,端口名0x1f3~0x1f5的作用在第一节已经给出,不在赘述。Shr指令为右移指令,作用是将一个寄存器或内存单元中的数据向右移指定位数,并将溢出标志 CF 作为进位标志。此处主要通过右移置换出地址写入相应的LBA寄存器。“and  al ,0x0f”将LBA的第24~27位放进device寄存器的低四位中,“or al,0xe0”把“0xe0”进行了或运算,拼出device寄存器的值,将高四位设置为“1110”,具体作用参考上一篇文章第一节内容。

         这段代码实现上一篇文章第三小节的第四个步骤,对2扇区进行读取操作。通过out指令写入command端口0x1F7,硬盘开始工作。

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第5张图片

         本段代码主要用于检测status寄存器的BSY状态,“nop”指令的功能与sleep函数相当,延迟一下从而减少打扰硬盘的工作。“in al,dx”将status寄存器的值读入到al寄存器中,通过“cmp  al,0x88”的与操作,保留第4位和第7位,第4位为1表示硬盘控制器已经准备好数据传输,第7位为1表示硬盘在忙,所以我们只要判断第4位是否为1即可,顾使用“cmp  al ,0x08”对第四位进行判断。最后使用“jnz not_ready”来判断结果是否等于0,若为0,则表示可以读数据了,反之则不断循环读取状态。

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第6张图片

         该段代码用于从0x1F0端口中读取数据,di寄存器中的值为要读取的扇区数,一个扇区有512个字节,data寄存器位16位,每次in操作读入2字节,所以需要指令di*512/2次,也就是di*256次。

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第7张图片

         这段代码用于把从扇区读取到的loader加载器写入到bx寄存器所指的内存(0x900)中,每读入2个字节,bx所致的地址便+2,一直循环。待执行完成后,“ret”跳出函数回到上面的第38行中去:

         也就是跳转到内存0x900的位置,loader加载器的位置,自此MBR的任务到此结束,完成了MBR与loader的过渡,并且将权限交付给loader。

四.LOADER加载器代码实现

         接下来我们来编写一个简易版的loader加载器,这个加载器主要是为了验证MBR是否能够顺利过渡到Loader这里,所以就让它显示一些信息出来就好了,至于如何从实模式到保护模式的过渡,并最终在保护模式下加载内核,等我们继续深入学习到其他东西后再进行代码的优化。

         话不多说,直接上代码:

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
        mov byte [gs:0x00],'A'
        mov     byte [gs:0x01],0xA4
        mov byte [gs:0x02],'K'
        mov     byte [gs:0x03],0xA4
        mov byte [gs:0x04],'A'
        mov     byte [gs:0x05],0xA4
        mov byte [gs:0x06],'H'
        mov     byte [gs:0x07],0xA4
        mov byte [gs:0x08],'A'
        mov     byte [gs:0x09],0xA4
        mov byte [gs:0x0a],'!'
        mov     byte [gs:0x0b],0xA4

jmp $

         这段代码其实不用太多的解析,因为之前和上一篇MBR程序(参考:【操作系统】优化MBR程序:让MBR调用显存吧)是一样的,只需要注意一下第2行中将逻辑编译地址(也即是动态重定向,有兴趣可以参考:【计算机组成】实模式/保护模式下地址分段(基段地址+偏移地址)的原因)定义在了LOADER_BASE_ADDR,也就是0x900处。

五.编译和运行

         到目前为止,我们整理下已经编写完成的代码并且着手开始编译:

  • 首先是改造版的MBR程序,为了方便记忆,本文命名为MBR.S
  • 然后是boot.inc配置信息文件
  • 再来就是新编写的loader程序,为了方便记忆命名为loader.S
  • 最后是虚拟硬盘hd60M.img,这个在之前的文章(参考:【操作系统】Bochs安装和配置)中有说明怎么生成,就不在次赘述了。

        我们首先编译MBR程序,使用以下指令即可进行简单的编译(注意你的硬盘、程序路径可能与我的不一样,请选择正确的路径):

        需要注意的是,Nasm的参数“-I”,意思是指定库目录的路径,现阶段也就是boot.inc的路径。假如如果你的boot.inc在AAA文件夹下,那么就写“nasm  –I  /AAA(文件夹的路径)  其他省略”,如果boot.inc和MBR在同一个路径下,就写“nasm   -I  ./   其他省略”,以此类推。

        接下来按照同样的方式编译loader加载器(注意你的硬盘、程序路径可能与我的不一样,请选择正确的路径):

        然后将MBR的二进制文件写入到0扇区中去(注意你的硬盘、程序路径可能与我的不一样,请选择正确的路径):

        最后再将loader加载器也写入到2扇区中去(注意你的硬盘、程序路径可能与我的不一样,请选择正确的路径):

        将代码写入到镜像后,我们再将镜像文件加入到Bochs的模拟硬件环境配置文件中,根据文章中所描述的,我们直接将新生成的镜像写入配置文件的以下位置即可:

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第8张图片

        保存一下修改后的配置文件,我们开始运行Bochs模拟器进行模拟操作:

./Bochs  –f   boch.disk(你自己的硬件配置文件名)

        运行成功后,会显示以下信息,并且默认为【6】:

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第9张图片

        此时我们再按一次回车,即可开始模拟:

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第10张图片

        我们在控制台中输入“c”(具体含义请查看上面所说的文章),继续往下运行,就能看到弹出的窗口中出现了我们所要的字符串:

【操作系统】调用硬盘并且实现MBR与Loader的过渡——实战篇_第11张图片​​​​​​​

你可能感兴趣的:(操作系统,linux,MBR,loader)