二. 编写mbr,让机器启动起来

mbr简介

大家都知道,在我们按下电脑电源的时候,首先启动的BIOS(基本输入输出系统),那么BIOS又是如何被启动的呢,谁来唤醒他呢,它又在何处运行呢。要了解这些的话,首先得介绍一下我们实模式的内存布局

实模式的内存布局

mark

图中的内容我们现在只需要关注红色框出来的地方,可以看到BIOS的入口地址处只有16BYTE的空间,很显然,这一小块空间肯定存放的不是数据,只能是指令了,图中也写的很明显了


jmp f000:e05b

也就是跳转到了(f000 << 4) + e05b = fe05b处,这里的段基址左移四位的原因是,在实模式下段基址寄存器只有16位,想一下,16位的寄存器最多访问2^16=64KB的空间,我们想访问实模式下1MB的空间的话就需要将段基址左移4位,自然就可以访问到1MB的空间了,这么做的原因也是出于兼容性而采取的曲线救国方式,虽然我们现在的OS都已经到了64位,它也还得向下兼容不是吗

当我们的电脑加电的一瞬间cs:ip就会被强制置位f000:e05b了,接下来就对内存,显卡等外设进行检查,做好它的初始化工作之后就完成它的任务了,在最后的时候,BIOS会通过绝对远跳


jmp 0:0x7c00

将接力棒交由MBR来加载我们的内核,我们初步的工作就是编写MBR。在进行内核加载之前,我们先通过MBR打印一些字符,来验证我们之前所说是否正确

编写MBR验证程序


;主引导程序

;---------------------



SECTION MBR vstart=0x7c00 ;程序开始的地址

    mov ax, cs            ;使用cs初始化其他的寄存器

    mov ds, ax            ;因为是通过jmp 0:0x7c00到的MBR开始地址

    mov es, ax            ;所以此时的cs为0,也就是用0初始化其他寄存器

    mov ss, ax            ;此类的寄存器不同通过立即数赋值,采用ax中转

    mov fs, ax

    mov sp, 0x7c00  ;初始化栈指针



;清屏利用0x10中断的0x6号功能

;------------------------

    mov ax, 0x600

    mov bx, 0x700

    mov cx, 0

    mov dx, 0x184f



    int 0x10

    ;获取光标位置

;---------------------

    mov ah, 3   ; 3号子功能获取光标位置

    mov bh, 1   ; bh寄存器存储带获取光标位置的页号,从0开始,此处填1可以看成将光标移动到最开始

    int 0x10



;打印字符串

;------------------

    mov ax, message

    mov bp, ax



    mov cx, 6    ;字符串长度,不包括'\0'

    mov ax, 0x1301

    mov bx, 0x2



    int 0x10



    jmp $



    message db "My MBR"

    times 510-($-$$) db 0 

    db 0x55, 0xaa

这段代码通过0x10号中断直接操控显卡,达到打印字符串的目的

编写好后通过


nasm -o mbr.bin mbr.S

dd if=mbr.bin of=/home/ba/bochs/hd60M.img bs=512 count=1 conv=notrunc

对我们的汇编代码进行编译并写入之前创建的磁盘中,接下来运行bochs,应该可以看到如下结果

mark

现在我们通过bochs的调试看一下程序到底是怎么执行的,和我们之前所说的是否一致

mark

这幅图是在我们开启bochs时显示的结果,很明显可以看到他的cs:ip寄存器的值和我们之前所说的结果一致,在这里进行跳转之后接下来肯定就是一系列的初始化工作了,我们跳过这些初始化的工作,直接进入到MBR执行的开始位置,也就是地址0x7c00处

mark

可以看到,左边是bochs初始化完成之后的输出,这是已经运行到了0x7c00后的结果,看红框标记的地方,有没有感觉很熟悉,这就是我们mbr的第一行代码啦,接下来就会按照我们所写的那样,清屏,打印了。

读取硬盘

前面通过打印字符串对开机启动过程做了个小小的验证,接下来需要让我们的MBR读取硬盘啦,因为加载kernel的话肯定需要从硬盘中读入数据


; 主引导程序

;---------------------



%include "boot.inc"



SECTION MBR vstart=0x7c00 ;程序开始的地址

    mov ax, cs            ;使用cs初始化其他的寄存器

    mov ds, ax            ;因为是通过jmp 0:0x7c00到的MBR开始地址

    mov es, ax            ;所以此时的cs为0,也就是用0初始化其他寄存器

    mov ss, ax            ;此类的寄存器不同通过立即数赋值,采用ax中转

    mov fs, ax

    mov sp, 0x7c00  ;初始化栈指针,sp也就是32位下的esp



;清屏利用0x10中断的0x6号功能

;------------------------

    mov ax, 0x600

    mov bx, 0x700

    mov cx, 0

    mov dx, 0x184f



    int 0x10



;获取光标位置

;---------------------

    mov ah, 3   ; 3号子功能获取光标位置

    mov bh, 1   ; bh寄存器存储带获取光标位置的页号,从0开始,此处填1可以看成将光标移动到最开始

    int 0x10



;打印字符串`

;------------------

    mov ax, message

    mov bp, ax



    mov cx, 6

    mov ax, 0x1301

    mov bx, 0x2



    int 0x10



    message db "My MBR"



    mov eax, LOADER_START_SECTOR    ;起始扇区的lba地址

    mov bx, LOADER_BASE_ADDR        ;写入的地址

    mov cx, 1                       ;读入的扇区数

    call rd_disk_m_16



    jmp LOADER_BASE_ADDR



;读取n个扇区

;---------------------

rd_disk_m_16:       ;eax=扇区号,cx=读入的扇区数,bx=将数据写入的内存地址

    mov esi, eax    ;备份eax和cx

    mov di, cx



;设置要读取的扇区数

    mov dx, 0x1f2

    mov al, al

    out dx, al

    mov eax, esi



;将lba地址存入0x1f3-0x1f6



    ;lba地址0-7位写入端口0x1f3

    mov dx, 0x1f3

    out dx, al



    ;lba地址8-15位写入端口0x1f4

    mov cl, 8

    shr eax, cl

    mov dx, 0x1f4

    out dx, al



    ;lba地址16-23位写入端口0x1f5

    shr eax, cl

    mov dx, 0x1f5

    out dx, al





    shr eax, cl

    and al, 0x0f

    or al, 0xe0

    mov dx, 0x1f6

    out dx, al



;向0x1f7端口写入读命令

    mov dx, 0x1f7

    mov al, 0x20

    out dx, al

.not_ready:

    nop

    in al, dx

    and al, 0x88 ;第4位为1表示硬盘控制器已经准备号数据传输,第7位为1表示硬盘忙

    cmp al, 0x08

    jnz .not_ready



;从0x1f0端口读数据

    mov ax, di

    mov dx, 256

    mul dx

    mov cx, ax

    mov dx, 0x1f0

.go_on_read:

    in ax, dx

    mov [bx], ax

    add bx, 2

    loop .go_on_read

    ret



    times 510-($-$$) db 0 

    db 0x55, 0xaa

这是boot.inc中的内容


LOADER_BASE_ADDR equ 0x900

LOADER_START_SECTOR equ 0x2

先看看程序的执行流程

  1. 从0x7c00入口处进入mbr

  2. 打印My MBR

  3. 为读取磁盘操作传递参数,包括读入的扇区数,读取的数据写入的内存地址

  4. 将读取到的数据写入0x900,并跳到此处去执行

MBR中的内容差不多就多了,接下来的工作就是逐步完善内核。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值