动手学操作系统(四、MBR读取硬盘加载Loader)

动手学操作系统(四、MBR读取硬盘加载Loader)

在上一节中,我们学习了使用MBR来直接控制显卡进行显示,在这一节中我们学习如何让MBR来操作硬盘,加载Loader来完成操作系统的后续启动过程。

文章目录

  • 动手学操作系统(四、MBR读取硬盘加载Loader)
    • 0. 通用寄存器的介绍
    • 1. 为什么需要Lodaer?
    • 2. 硬盘介绍
    • 3. MBR读取硬盘加载Loader
    • Reference

0. 通用寄存器的介绍

介绍一些常用的寄存器,方便以后查找

Image

1. 为什么需要Lodaer?

我们的MBR受限于512字节的大小,在如此小的空间中,我们无法为操作系统的内核准备好完整的运行环境,更没办法将内核成功加载到内存并运行,所以我们需要另一个程序来完成初始化环境及加载内核的工作,这个程序就被称为Loader,简而言之就是,MBR负责从硬盘上把Loader加载到内存中,然后将控制权交给Loader,在Loader中要定义一些数据结构,这些数据结构在将来的内核中还是需要使用的,所以将Loader加载到内存中时不能进行覆盖。我们尽量将Loader放置在低地址处,多留一些空间给内核。

2. 硬盘介绍

硬盘属于存储介质,在硬盘的发展历史中,随机存取具有划时代的意义,程序中的算法不用再考虑存储时间,访问任意数据所用的时间几乎是相等的,这一改之前的存储设备其存取时间呈线性的历史。

为了让硬盘工作,我们需要通过读写硬盘控制器的端口,端口就是位于IO控制器上的寄存器,这里就算指的是硬盘控制器上的寄存器。硬盘的控制遵循标准ATA(Advanced Technology Attachment),ATA的标准有些冗长,这里只简单介绍我们需要使用到的部分,如下图所示

Image

3. MBR读取硬盘加载Loader

由于MBR占据了硬盘的第0扇区,则第1扇区是空闲的,为了安全性,间隔1个扇区,将Loader放置在第2扇区,参考实模式下的1MB内存布局,

Image

0x500~0x7BFF0x7E00~0x9FBFF这两段内存区域都是可以使用的,为了给内核留足够的空间,这里将Loader加载到内存地址的0x900处(当然,你也可以放置在别处)。

接下来就编写程序来进行加载,在编写程序之前,我们先优化一下我们的工程路径结构,将工程路径优化为如下,在src路径下新增了一个boot目录用于管理我们boot过程中的代码文件,然后在boot目录中增加一个lib目录用于管理我的一些配置文件比如boot.inc,然后新建一个loader.S用于编写Loader的代码,bin目录用于存放编译的结果,整个工程的目录如下所示,当然你也可以按照你喜欢的方式来进行组织

Image

分别写入以下的内容

mbr.S

; ~/d2los/src/boot/mbr.S
; 主引导程序 
; LOADER_BASE_ADDR equ 0xA000 
; LOADER_START_SECTOR equ 0x2
;------------------------------------------------------------
%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=24,0x4f=79
   int     10h                     ; int 10h

   ; 输出字符串: Hello World
   mov byte [gs:0x00],'H'
   mov byte [gs:0x01],0xA4     ; A表示绿色背景,4表示前景色为红色
   mov byte [gs:0x02],'e'
   mov byte [gs:0x03],0xA4
   mov byte [gs:0x04],'l'
   mov byte [gs:0x05],0xA4
   mov byte [gs:0x06],'l'
   mov byte [gs:0x07],0xA4
   mov byte [gs:0x08],'o'
   mov byte [gs:0x09],0xA4

   mov byte [gs:0x0A],' '
   mov byte [gs:0x0B],0xA4

   mov byte [gs:0x0C],'W'
   mov byte [gs:0x0D],0xA4
   mov byte [gs:0x0E],'o'
   mov byte [gs:0x0F],0xA4
   mov byte [gs:0x10],'r'
   mov byte [gs:0x11],0xA4
   mov byte [gs:0x12],'l'
   mov byte [gs:0x13],0xA4
   mov byte [gs:0x14],'d'
   mov byte [gs:0x15],0xA4
     
   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=LBA扇区号
                                   ; ebx=将数据读入的内存地址
                                   ; ecx=读取的扇区数
      mov esi,eax    ;保存eax
      mov di,cx      ;保存cx
;读入硬盘:
;1:设置要读取的扇区数
      mov dx,0x1f2
      mov al,cl
      out dx,al            ;读取的扇区数

      mov eax,esi   ;恢复ax

;2:将LBA地址存入0x1f3 ~ 0x1f6端口

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

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

      ;LBA地址23~16位写入端口0x1f5
      shr eax,cl
      mov dx,0x1f5
      out dx,al

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

;3:0x1f7端口写入读取命令0x20 
      mov dx,0x1f7
      mov al,0x20                        
      out dx,al

;4:检查硬盘状态
  .not_ready:
      ;同一个端口,写时表示写入命令字,读时表示读取硬盘状态
      nop
      in al,dx
      and al,0x88   ;4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
      cmp al,0x08
      jnz .not_ready   ;如果未准备好,继续等待

;5:0x1f0端口读取数据
      mov ax, di
      mov dx, 256
      mul dx
      mov cx, ax   ; di为要读取的扇区数,一个扇区有512字节,每次读取一个字,
                   ; 共需di*512/2次,所以di*256
      mov dx, 0x1f0
  .go_on_read:
      in ax,dx
      mov [bx],ax
      add bx,2     
      loop .go_on_read
      ret
   
   times 510-($-$$) db 0   ; 剩余部分填充0
   db 0x55,0xaa

boot.inc

; ~/d2los/src/boot/lib/boot.inc
;-------------	 loader和kernel   ----------
LOADER_BASE_ADDR equ 0x900 
LOADER_START_SECTOR equ 0x2

loader.S

; ~/d2los/src/boot/loader.S
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR

; 清屏
; 利用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=24,0x4f=79
   int     10h                     ; int 10h
   
; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
mov byte [gs:0x00],'H'
mov byte [gs:0x01],0xA4     ; A表示绿色背景闪烁,4表示前景色为红色
mov byte [gs:0x02],'i'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],' '
mov byte [gs:0x05],0xA4   
mov byte [gs:0x06],'L'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'o'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0a],'a'
mov byte [gs:0x0b],0xA4
mov byte [gs:0x0c],'d'
mov byte [gs:0x0d],0xA4
mov byte [gs:0x0e],'e'
mov byte [gs:0x0f],0xA4
mov byte [gs:0x10],'r'
mov byte [gs:0x11],0xA4

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

然后编译

cd ~/d2los
nasm -I src/boot/lib -o bin/mbr.bin src/boot/mbr.S
nasm -I src/boot/lib -o bin/loader.bin src/boot/loader.S

这里关键-I的作用是,添加一个包含文件的目录,其实就是添加你的库目录。

然后装载到硬盘

cd ~/d2los
dd if=./bin/mbr.bin of=~/bochs/bin/hardisk60MB.img bs=512 count=1 seek=0 conv=notrunc
dd if=./bin/loader.bin of=/home/sjh/bochs/bin/hardisk60MB.img bs=512 count=2 seek=1 conv=notrunc

这里的关键字解释如下:

bs=512 指定块大小为 512 字节(一个扇区大小)。
count=1 仅复制一个块(一个扇区)。
seek=0 从输出文件的开头(第一个扇区)开始写入。
conv=notrunc 保持文件大小不变。

bs=512 指定块大小为 512 字节。
count=2 复制两个块(两个扇区)。
seek=1 从输出文件的第二个扇区开始写入(第一个扇区之后)。

正如我们之前所说的,我们将编写好的Loader代码装载到硬盘的第二扇区,模拟其一直存储在硬盘中,然后使用MBR读取硬盘加载Loader,从而将控制权交给Loader,让Loader继续完成其功能。

然后运行

~/bochs/bin/bochs -f ~/bochs/bin/bochsrc.disk 

如果写入有误,可以使用命令

dd if=/dev/zero of=~/bochs/bin/hardisk60MB.img bs=1M count=60

来清空虚拟硬盘。

为了方便起见,我们可以将上述的命令写成.sh文件,方便以后直接使用

command_compile.sh

nasm -I src/boot/lib -o bin/mbr.bin src/boot/mbr.S
nasm -I src/boot/lib -o bin/loader.bin src/boot/loader.S
dd if=./bin/mbr.bin of=~/bochs/bin/hardisk60MB.img bs=512 count=1 seek=0 conv=notrunc
dd if=./bin/loader.bin of=/home/sjh/bochs/bin/hardisk60MB.img bs=512 count=2 seek=1 conv=notrunc

command_run.sh

~/bochs/bin/bochs -f ~/bochs/bin/bochsrc.disk 

效果如下

Image

然后我们详细解释一下代码,在boot.inc文件中,我们定义了两个宏分别是LOADER_BASE_ADDRLOADER_START_SECTORequ是用于定义宏的关键字类似于C语言中的#define

Reference

[1]《一个64位操作系统的设计与实现》
[2]《操作系统真象还原》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/662817.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

005、API_数据结构

键的数据结构类型,它们分别是: string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集 合),这些只是Redis对外…

pg_lakehouse 与 datafusion

原理分析 pg_lakehouse 是 ParadeDB 推出的一个开源插件,支持对多种数据湖里的数据做分析计算。它的出现,使得 Postgres 能够像访问本地数据一样轻松访问 S3 等对象存储,轻松访问 Delta Lake 上的表格,具备数据湖分析能力。 pg_…

ES报错1

ES在kibana的JSON如图: 提交后错误信息如下 所以是什么错误呢: 原来是:json的格式有误改成 这里的错误其实是我在文件传输时,为了节约空间,没有以json格式传递,而是一串字符就传过来了,需要使用josn的格式化工具格式化才行,结果格式化的不正确,才遇到此坑

Guns框架:基于主流技术Spring Boot2 + Vue3 + Antd Vue的现代Java应用开发新纪元

Guns框架:基于主流技术Spring Boot2 Vue3 Antd Vue的现代Java应用开发新纪元 摘要:随着信息技术的飞速发展,软件开发框架在提升开发效率、降低成本方面扮演着至关重要的角色。Guns框架,作为一个现代化的Java应用开发框架&#x…

【Linux多线程】认识多线程创建线程

文章目录 什么是多线程为什么称linux下的线程是轻量级进程呢? 线程的优点线程的缺点线程异常线程和进程创建线程1.pthread_create2.pthread_self 什么是多线程 进程是正在运行的程序的实例,而线程(thread)是进程中的一个执行路线…

如何顺利通过软考中级系统集成项目管理工程师?

中级资格的软考专业包括"信息系统",属于软考的中级级别。熟悉软考的人都知道,软考分为初级、中级和高级三个级别,涵盖计算机软件、计算机网络、计算机应用技术、信息系统和信息服务五个专业,共设立了27个资格。本文将详…

GPT-4o:重塑人机交互的未来

一个愿意伫立在巨人肩膀上的农民...... 一、推出 在人工智能(AI)领域,自然语言处理(NLP)技术一直被视为连接人类与机器的桥梁。近年来,随着深度学习技术的快速发展,NLP领域迎来了前所未有的变革…

Excel快速判断大量身份证性别,VBS代码

身份证判断性别的原理就是,身份证倒数第二位是单数表示是男的,单数是女的 可以用IF公式来判断,但是需要下拉,如果几百上千条数据还好,要是上万就不好拉取了,如果数据太多,可以用VBA代码判断 IF(MOD(VALUE(MID(A1,17,1)),2)0,"女","男") 原理:MID(A1,17,1…

MacBook 怎么玩Windows游戏 苹果笔记本怎么玩游戏?mac上如何玩windows游戏

传统上,Mac 不被认为是好的游戏机。然而,苹果已经开始在 Mac 上的游戏上投入更多精力,特别是自从转向苹果芯片以来。这使得 Mac 游戏的本机移植数量和模拟 Windows 游戏的能力都得到了显著提高。 方法一:Boot Camp 1、Boot Camp是…

正确地安装 Python

Python由荷兰国家数学与计算机科学研究中心的吉多范罗苏姆于1990年代初设计,作为一门叫做ABC语言的替代品。Python提供了高效的高级数据结构,还能简单有效地面向对象编程。Python语法和动态类型,以及解释型语言的本质,使它成为多数…

windows10远程桌面端口,修改Windows 10远程桌面端口的步骤

在Windows 10操作系统中,远程桌面功能为企业用户、技术支持人员以及个人用户提供了极大的便利,允许他们远程访问和管理另一台计算机的桌面环境。然而,默认的远程桌面端口(通常为3389)常常成为安全漏洞的潜在目标&#…

粒子辐照环境中相机镜头防护及LabVIEW图像处理注意事项

在粒子辐照环境测试电路板性能的实验中,需要对相机镜头进行有效防护,同时利用LabVIEW进行图像识别和处理。本文将讨论相机镜头防护的关键因素和LabVIEW处理过程中的注意事项,包括防辐射材料选择、辐射屏蔽措施、散热管理、空间布局及LabVIEW软…

Linux驱动开发笔记(二) 基于字符设备驱动的GPIO操作

文章目录 前言一、设备驱动的作用与本质1. 驱动的作用2. 有无操作系统的区别 二、内存管理单元MMU三、相关函数1. ioremap( )2. iounmap( )3. class_create( )4. class_destroy( ) 四、GPIO的基本知识1. GPIO的寄存器进行读写操作流程2. 引脚复用2. 定义GPIO寄存器物理地址 五、…

SC8205LA 20VN沟道增强型MOS(Mos)场效应管

特点 ❥专有的先进平面技术 ❥高密度超低电阻设计 ❥大功率、大电流应用 ❥理想的锂电池应用 ❥封装形式:SOT23-6

总是不能盈利?试着用这两个观点去学习现货白银的技巧

一进入现货白银市场,投资者都想着如何去找到现货白银交易的机会,学习现货白银投资的方法。其实这些都是手段,而最终的目的还是为我们的盈利服务。而对于盈利来说,其实胜率和风险报酬比才是影响盈利的重要因素,我们带着…

Springboot邮件发送配置

Springboot邮件发送配置 pom.xml依赖&#xff1a; <dependency><groupId>org.eclipse.angus</groupId><artifactId>jakarta.mail</artifactId><version>2.0.3</version> </dependency> <dependency><groupId>or…

最简单的安卓模拟器抓包?

安装模拟器抓包似乎是有个绕不开的话题&#xff0c;但是现在普遍的安卓模拟器抓包会遇到以下问题&#xff1a; 1.证书配置繁琐 2.模拟器不兼容软件 3.系统设置繁琐。 前几天写过一次微信小程序如何抓包&#xff0c;现在来讲一下模拟器怎么抓包吧。首先使用的工具还是TangGo测…

Canny算子

Canny算子_百度百科 (baidu.com)https://baike.baidu.com/item/Canny%E7%AE%97%E5%AD%90/8821789?frge_ala 图像处理中最经典的边沿检测算法&#xff1a; Canny边缘检测_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1U4411277i/?spm_id_from333.1007.top_right_bar_…

达梦 结果拼接=多行结果返回一列字符串.

sql 转换 查询出多行数据 (select t.PROPERTY from JD_CODING t left join DELIVERY_OF c on t.VALUE c.TYPE where t.PROPERTY stackingType group by t.PROPERTY) 更改后 转为一列的拼接字符串 ( select listagg( distinct t.PROPERTY,,) within group ( order by t.P…

linux的磁盘分区与管理

磁盘分区与管理 识别磁盘 一块硬盘的“艺术”之旅&#xff08;硬盘空间使用&#xff0c;经历的步骤&#xff09; •识别硬盘 > 分区规划 > 格式化 > 挂载使用 Unix/Linux的基本哲学理念&#xff1a;一切皆文件 /dev:存放设备&#xff08;键盘、鼠标、光驱、硬盘……