• 自己动手从零写桌面操作系统GrapeOS系列教程——20.汇编语言读硬盘实战


    学习操作系统原理最好的方法是自己写一个简单的操作系统。


    本讲我们设计一个简单的读硬盘实验。通过一定的方法使硬盘第二个扇区的前3个字节依次为1、2、3,最后3个字节依次为3、2、1,中间的506个字节全为0。然后通过读硬盘程序将硬盘第二个扇区的数据读取到内存0x7e00-0x7fff的地方,也就是内存中MBR之后的512个字节。最后通过QEMU+DGB调试的方式来查看内存中0x7e00-0x7fff的数据,是否与硬盘第二个扇区中的数据一致,如果一致说明读硬盘成功。
    本讲代码文件共2个:

    • data1.asm
    • boot1.asm

    下面我们开始实验。

    一、设置硬盘第二个扇区中的数据

    data1.asm代码如下:

    db 1
    db 2
    db 3
    times 506 db 0
    db 3
    db 2
    db 1
    

    data1.asm就是生成512字节的数据,前3个字节依次是1、2、3,最后3个字节依次为3、2、1,中间的506个字节全为0。
    在PowerShell中输入如下命令:

    nasm data1.asm -o data1.bin
    hexdump data1.bin -C
    

    上述命令是将data1.asm通过汇编器生成了二进制文件data1.bin,然后通过hexdump命令查看data1.bin是否正确。截图如下:

    从上面截图可以看到,生成的data.bin文件共512字节,其中前3个字节依次为1、2、3,最后3个字节依次为3、2、1,中间的506个字节全为0。下面将data1.bin文件写入到虚拟硬盘的第二个扇区中。
    在PowerShell中输入如下命令:

    dd conv=notrunc if=data1.bin of=/media/VMShare/GrapeOS.img seek=1
    hexdump /media/VMShare/GrapeOS.img -C
    

    上面dd命令中的参数seek=1意思是在写入GrapeOS.img时,跳过1个写入块,写入块默认大小为512字节,也就将data1.bin写入到虚拟硬盘GrapeOS.img的第二个扇区中。截图如下:

    从上面截图中可以看到,虚拟硬盘第二个扇区中的数据达到了我们的实验要求。

    二、读硬盘程序

    boot1.asm的代码如下:

    ;定义常量
    DISK_BUFFER equ 0x7e00 ;读硬盘临时存放数据用的缓存区,放到boot程序之后。
    
    org 0x7c00
    
    ;初始化段寄存器
    mov ax,cs
    mov ds,ax ;ds指向与cs相同的段
    
    mov esi,1 ;读取硬盘的第2个扇区
    mov di,DISK_BUFFER
    call func_read_one_sector
    
    stop:
    hlt
    jmp stop 
    
    ;读取硬盘1个扇区(主硬盘控制器主盘)
    ;输入参数:esi,ds:di。
    ;esi LBA扇区号
    ;ds:di 将数据写入到的内存起始地址
    ;输出参数:无。
    func_read_one_sector:
    ;第1步:检查硬盘控制器状态
    mov dx,0x1f7
    .not_ready1:
    nop ;nop相当于稍息 hlt相当于睡觉
    in al,dx ;读0x1f7端口
    and al,0xc0 ;第7位为1表示硬盘忙,第6位为1表示硬盘控制器已准备好,正在等待指令。
    cmp al,0x40 ;当第7位为0,且第6位为1,则进入下一个步。
    jne .not_ready1 ;若未准备好,则继续判断。
    ;第2步:设置要读取的扇区数
    mov dx,0x1f2
    mov al,1
    out dx,al ;读取1个扇区
    ;第3步:将LBA地址存入0x1f3-0x1f6
    mov eax,esi
    ;LBA地址7-0位写入端口0x1f3
    mov dx,0x1f3
    out dx,al
    ;LBA地址15-8位写入端口写入0x1f4
    shr eax,8
    mov dx,0x1f4
    out dx,al
    ;LBA地址23-16位写入端口0x1f5
    shr eax,8
    mov dx,0x1f5
    out dx,al
    ;第4步:设置device端口
    shr eax,8
    and al,0x0f ;LBA第24-27位
    or al,0xe0 ;设置7-4位为1110,表示LBA模式,主盘
    mov dx,0x1f6
    out dx,al
    ;第5步:向0x1f7端口写入读命令0x20
    mov dx,0x1f7
    mov al,0x20
    out dx,al
    ;第6步:检测硬盘状态
    .not_ready2:
    nop ;nop相当于稍息 hlt相当于睡觉
    in al,dx ;读0x1f7端口
    and al,0x88 ;第7位为1表示硬盘忙,第3位为1表示硬盘控制器已准备好数据传输。
    cmp al,0x08 ;当第7位为0,且第3位为1,进入下一步。
    jne .not_ready2 ;若未准备好,则继续判断。
    ;第7步:从0x1f0端口读数据
    mov cx,256 ;每次读取2字节,一个扇区需要读256次。
    mov dx,0x1f0
    .go_on_read:
    in ax,dx
    mov [di],ax
    add di,2
    loop .go_on_read
    ret
    
    times 510-($-$$) db 0
    db 0x55,0xaa
    

    上面的代码主要就是读硬盘函数func_read_one_sector,总共分7个步骤,与上讲中的读硬盘操作步骤一致。结合代码注释和之前讲的知识,大家应该是可以看懂的。
    下面我们将编译boot1.asm,并将生成的二进制文件写入到虚拟硬盘的第一个扇区。在PowerShell中输入如下命令:

    nasm boot1.asm -o boot1.bin
    dd conv=notrunc if=boot1.bin of=/media/VMShare/GrapeOS.img
    

    截图如下:

    在Windows的cmd命令行中启动QEMU调试模式:

    qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img -S -s
    

    截图如下:

    在PowerShell中启动GDB:

    [root@CentOS7 Lesson20]# gdb
    (gdb) target remote 192.168.10.102:1234
    (gdb) b *0x7c00
    (gdb) c
    Continuing.
    
    Breakpoint 1, 0x00007c00 in ?? ()
    (gdb) x /512xb 0x7e00
    

    截图如下:

    从截图中可以看到,此时程序停在了断点0x7c00处,此时内存中0x7e00-0x7fff中的数据都是0。
    我们输入命令c,让程序继续运行几秒钟,然后Ctrl+C,让程序暂停。此时读硬盘程序应该已经执行完了,我们再来查看一下内存0x7e00-0x7fff中的数据。截图如下:

    从截图中可以看到,此时内存中0x7e00-0x7fff的数据和硬盘第二扇区中的数据一致,说明读取成功,实验完毕。


    本讲视频版地址:https://www.bilibili.com/video/BV1JY411z7VT/
    配套的代码与资料在:https://gitee.com/jackchengyujia/grapeos-course
    GrapeOS操作系统交流QQ群:643474045

  • 相关阅读:
    MySQL的常用优化方案
    牛客每日刷题
    一文看懂推荐系统:物品冷启05:流量调控
    计划跳槽需要做哪些准备?
    电动剃须刀市场现状研究分析与发展前景预测
    C++算法之旅、06 基础篇 | 第三章 图论
    C++ 背包问题——多重背包
    写论文工具:LaTex在线网站
    ios获取原生系统应用的包
    0037【Edabit ★☆☆☆☆☆】【修改Bug 2】Buggy Code (Part 2)
  • 原文地址:https://www.cnblogs.com/chengyujia/p/17238879.html