从零开始操作系统------MBR直操硬盘、内核加载器

本文基于郑纲的《操作系统还原》,仅为个人学习笔记,前期的虚拟机配置等不再详细记录,其中不理解或者出错的地方还望提出意见!

从零开始操作系统------MBR直操显卡

硬盘

在这里插入图片描述

  1. 存储逻辑
    为了更有效管理磁盘,将整个盘面划分为多个同心环,以圆心画扇形,扇形与每个同心环相交的弧状区域作为最基本的数据存储单元。这个同心环就称为磁道,而同心环上的弧状区域是扇形的一部分,故称之为扇区,它作为我们向硬盘存储数据的最基本单位,大小是 512 字节。我们写入的数据最终是写进了磁道上的扇区中。

    扇区是有自己的“头部”,头部之后的部分才是存储数据的 512 字节。头部中包含了扇区自身的信息:磁头号、磁道号和扇区号。

  2. 硬盘控制器端口
    在这里插入图片描述

    由表可知,端口用途在读硬盘和写硬盘时是有一点不同的。
    1、读硬盘时,端口 0x171 或 0x1F1 的寄存器名字叫 Error 寄存器,只在读取硬盘失败时有用,里面才会记录失败的信息,尚未读取的扇区数在 Sector count 寄存器中。在写硬盘时,此寄存器有了别的用途,所以有了新的名字,叫 Feature 寄存器。有些命令需要指定额外参数,这些参数就写在 Fea ture 寄存器中。
    2、在读硬盘时,端口 0x1F7 或 0x177 的寄存器名称是 Status,它是 8 位宽度的寄存器,用来给出硬盘的状态信息。在写硬盘时,端口 0x1F7 或 0x177 的寄存器名称是 command。此寄存器用来存储让硬盘执行的命令,只要把命令写进此寄存器,硬盘就开始工作了
    在这里插入图片描述
    device 寄存器是个杂项,它的宽度是 8 位。在此寄存器的低 4 位用来存储 LBA 地址的第 24~27 位。结合上面的三个 LBA 寄存器。第 4 位用来指定通道上的主盘或从盘, 0 代表主盘, 1 代表从盘。第 6 位用来设置是否启用 LBA 方式, 1 代表启用 LBA 模式, 0 代表启用 CHS 模式。另外的两位:第 5 位和第 7 位是固定为 1 的,称为 MBS 位

  3. 硬盘操作方法
    最主要的顺序就是 command 寄存器一定得是最后写,因为一旦 command 寄存器被写入后,硬盘就开始干活。
    操作步骤:
    ( 1)先选择通道,往该通道的 sector count 寄存器中写入待操作的扇区数。
    ( 2)往该通道上的三个 LBA 寄存器写入扇区起始地址的低 24 位。
    ( 3)往 device 寄存器中写入 LBA 地址的 24~27 位,并置第 6 位为 1,使其为 LBA 模式,设置第 4
    位,选择操作的硬盘( master 硬盘或 slave 硬盘)。
    ( 4)往该通道上的 command 寄存器写入操作命令。
    ( 5)读取该通道上的 status 寄存器,判断硬盘工作是否完成。
    ( 6)如果以上步骤是读硬盘,进入下一个步骤。否则,完工。
    ( 7)将硬盘数据读出。

MBR操作硬盘

由于MBR只有512字节大小,无法为内核准备好环境,也无法将内核加载到内存并运行。于是通过单独的一个程序完成环境初始化以及内核的加载,即Loader,内核加载器。MBR的的任务则是负责将loader从硬盘上加载到内存中,并将接力棒交给它。
MBR 是占据了硬盘的第 0 扇区(以逻辑 LBA 方式,扇区从 0 开始编号,若是以物理 CHS 方式,扇区则从 1 开始编号),第 1 扇区是空闲的,可以用,但离得太近总感觉不如隔开一点心里踏实,所以把loader 放到第 2 扇区。 MBR 从第 2 扇区中把它读出。
loader 中要定义一些数据结构,如 GDT 全局描述符表,这些数据结构将来的内核还是要用的,所以 loader 加载到内存后不能被覆盖。随着不断添加功能,内核必然越来越大,其所在的内存地址也会向越来越高的地方发展,难免会超过可用区域的上限,咱们尽量把 loader 放在低处,多留出一些空间给内核,将 loader 的加载地址选为 0x900。

  1. 改造MBR
;  主引导程序
;---------------------------------------------------------------------------------------
%include "boot.inc"
SECTION MBR vstart=0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800
    mov gs,ax

;清屏 
;利用0x06号功能,上卷全部行,则可清屏
;---------------------------------------------------------------------------------------
;  INT 0x10   功能号:0x06  功能描述: 上卷窗口
;---------------------------------------------------------------------------------------
;  输入:
;  AH  功能号= 0x06
;  AL  = 上卷的行数(若为0,表示全部)
;  BH  = 上卷行属性
;  (CL,CH) = 窗口左上角的(X,Y)位置
;  (DL,DH) = 窗口右下角的(X,Y)位置
;  无返回值
    mov ax,0600h
    mov bx,0700h
    mov cx,0          ; 左上角:(0,0)
    mov dx,184fh      ; 右下角:(80,25)
                  ; VGA文本模式中,一行只能容纳80个字符,共25; 下标0开始,所以0x18=240x4f=79
    int 10h            ; int 0x10
;jmp $               ; 使程序悬停在此

; 输出背景色绿色,前景色红色,并且跳动从字符串“1 MBR”

    mov byte [gs:0x00],'1'
    mov byte [gs:0x01],0xA4     ; A表示绿色背景闪烁,4表示前景色为红色

    mov byte [gs:0x02],' '
    mov byte [gs:0x03],0xA4

    mov byte [gs:0x04],'M'
    mov byte [gs:0x05],0xA4

    mov byte [gs:0x06],'B'
    mov byte [gs:0x07],0xA4

    mov byte [gs:0x08],'R'
    mov byte [gs:0x09],0xA4

    mov eax,LOADER_START_SECTOR   ; 起始扇区lba地址
    mov bx,LOADER_BASE_ADDR       ; 写入的地址
    mov cx,4                      ; 待读入的扇区数
    call rd_disk_m_16             ; 以下读取程序的起始部分(一个扇区)

    jmp LOADER_BASE_ADDR + 0x300                     

;-----------------------------------------------------------------------------------
;功能:读取硬盘的n个扇区
rd_disk_m_16:
;-----------------------------------------------------------------------------------
; eax=LBA扇区号
; bx=将数据写入内存地址
; cx=读入的扇区数
    mov esi,eax                   ; 备份eax
    mov di,cx                     ; 备份cx

; 读写硬盘:
; 第一步:设置要读取的扇区数
    mov dx,0x1f2
    mov al,Cl
    out dx,al                     ; 读取的扇区数

    mov eax,esi                   ; 恢复ax

; 第二步:将LBA地址存入0x1f3~0x1f6

    ; LBA地址70位写入端口0x1f3
    mov dx,0x1f3
    out dx,al

    ; LBA地址158位写入端口0x1f4
    mov cl,8
    shr eax,cl
    mov dx,0x1f4
    out dx,al

    ; 将LBA地址2316位写入端口0x1f6
    shr eax,cl
    mov dx,0x1f5
    out dx,al

    shr eax,cl
    and al,0x0f                  ; lba第2427位
    or al,0xe0                   ; 设置74位为1110,表示lba模式
    mov dx,0x1f6
    out dx,al

; 第三步:向0x1f7端口写入命令,0x20
    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端口读数据
    ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字共需di*512/2次,所以di*256
    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

  1. 配置文件boot.inc
;----------------------- loader and kernel -----------------------

LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2 

nasm 提供的宏, 和 C 语言中的宏是一回事。 只不过 nasm中的语法是:宏名 equ 值,
LOADER_BASE_ADDR 定义了 loader 在内存中的位置, MBR 要把 loader 从硬盘读入后放到此处。如前所述,它的值是 0x900,说明将来 loader 会在内存地址 0x900 处。
LOADER_START_SECTOR 定义了 loader 在硬盘上的逻辑扇区地址,即 LBA 地址。它等于 0x2,说明 loader 放在了第 2 块扇区。

内核加载器

loader 是要经过实模式到保护模式的过渡,并最终在保护模式下加载内核。这里只是一个简单版本。

%include "boot.inc"
 section loader vstart=LOADER_BASE_ADD

 ; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
 mov byte [gs:0x00],'2'
 mov byte [gs:0x01],0xA4 ; A 表示绿色背景闪烁, 4 表示前景色为红色

 mov byte [gs:0x02],' '
 mov byte [gs:0x03],0xA4

 mov byte [gs:0x04],'L'
 mov byte [gs:0x05],0xA4

 mov byte [gs:0x06],'O'
 mov byte [gs:0x07],0xA4

 mov byte [gs:0x08],'A'
 mov byte [gs:0x09],0xA4

 mov byte [gs:0x0a],'D'
 mov byte [gs:0x0b],0xA4

 mov byte [gs:0x0c],'E'
 mov byte [gs:0x0d],0xA4

 mov byte [gs:0x0e],'R'
 mov byte [gs:0x0f],0xA4

 jmp $ ; 通过死循环使程序悬停在此

实验结果

在这里插入图片描述
实验结果如上。

从零开始操作系统------探析保护模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值