实验目的:

• 如何从软盘读取并加载一个Loader程序到操作

系统,然后转交系统控制权

• 对应章节:第四章

实验内容:

1. 向软盘镜像文件写入一个你指定的文件,手

工读取在磁盘中的信息

2. 在软盘中找到指定的文件,读取其扇区信息

3. 将指定文件装入指定内存区,并执行

4. 学会在bochs中使用xxd读取反汇编信息

完成本次实验要思考的问题:

1.FAT12格式是怎样的?

2.如何读取一张软盘的信息

3.如何在软盘中找到指定的文件

4.如何在系统引导过程中,从读取并加载一个可执行文件

到内存,并转交控制权?

5.为什么需要这个Loader程序不包含dos系统调用?

关键技术:

引导扇区,loader与控制权转交。

一个操作系统从开机到开始运行,大 致经历“引导→加载内核入内存→跳入保护模式→开始执行内核”这样一个过程。也就是说,在内核开始执行之前不但要加载内 核,而且还有准备保护模式等一系列工作,如果全都交给引导扇区来做,512字节很可能是不够用的,所以,把这个过程交给另外的模块来完成,我们把这个模块叫做Loader。引导扇区负责把Loader加载入内存并且把控制权交给它,其他工作交给 Loader来做,因为它没有512字节的限制,将会灵活得多。

在这里,为了操作方便,把软盘做成FAT12格式,这样对Loader以及今后的Kernel(内核)的操作将会非常简单易行。

实验步骤:

1. 向软盘镜像文件写入一个你指定的文件,手工读取在磁盘中的信息

(1)修改引导扇区

增加BPB等信息以被识别。修改boot.asm,生成boot.bin,写入已引导扇区。(boot.asm在第一章出现)

把生成的Boot.bin写入磁盘引导扇区,运行的效果没有变,仍然会是图1.1的样子。但是,现在的软盘已经能够被DOS以及 Linux识别了,我们已经可以方便地往上添加或删除文件了。

修改bochsrc:

修改vgaromimage对应的文件位置,以你的实际安装位置为准

注释掉keyboard_mapping一行

增加display_library: sdl

(2)一个简单的loader

Loader.asm。编译为loader.bin

(3)读软盘,根目录部分得loader起始扇区号

为加载loader.bin到软盘,需要读软盘。

核心思想为修改boot.asm,引导扇区,使其功能改为读软盘,寻找loader.bin

用bios中断 int 13h读软盘。

中断需要的参数不是原来提到的从第0扇区开始的扇区号,而是柱面号、磁头号以及在当前柱面上的扇区号3个分量,所以需要我们自己来转换一下。对于1.44MB的软盘来讲,总共有两面(磁头号0和1),每面80个磁道(磁道号0~79),每个 磁道有18个扇区(扇区号1~18)。下面的公式就是软盘容量的由来: 2×80×18×512=1.44MB

于是,磁头号、柱面(磁道)号和起始扇区号可以用图所示的方法来计算。

注意如Q=0,1,2,3,4,0与1为柱面0,在两面为磁道0.0为磁头0,1为磁头1.

可知写软盘时先写一个柱面,上下磁道都写满了再切换柱面。

对应,写读软盘函数到boot.asm。

由于上述代码用到堆栈,故有初始化堆栈,初始化ss和esp

之后写查找loader.bin的函数

遍历根目录区所有的扇区,将每一个扇区加载入内存,然后从中寻找文件名为Loader.bin的条目,直到找到为止。找到的那一刻,es:di是指向条目中字母N后面的那个字符。其中宏定义与变量

由于在读取过程中打印一些字符串,我们需要一个函数来做这项工作。为了节省代码长度,字符串的长度都设为9字节,不够则用空格补齐,这样就相当于一个二维数组,定位的时候通过数字就可以了。显示字符串的函数DispStr,调用它的时候只要保证寄存器dh的值是字符串的序号就可以了。

(4)写入boot.bin,loader.bin到软盘并反汇编调试

写入:

但此时boot.bin只是找到了loader.bin,运行不会有效果,所以加断点反汇编调试。

b 0x7c00 是因为bios把boot sector加载到0x7c00处。见boot.asm

N 单步执行,遇到函数则跳过。这里跳过了BPB

U 反汇编。/45 为count,反汇编的指令个数。用help x可以查看信息

然后书上b 0x7cb4是在boot.bin的jmp $处下断点,根据实际情况(0x7cad处是jmp.-2,jmp $),我在b 0x7cad处下断点,然后

x /32xb es:di - 16 ←查看es:di 前后的内存

x /13xcb es:di - 11 ←容易发现es:di 前乃我们要找的文件名

sreg ←查看es

r查看di

可见拷贝成功。

(5)根据(3)读根目录得到的扇区号,读FAT将loader加载到内存

继续修改boot.asm。

现在我们已经有了Loader.bin的起始扇区号,我们需要用这个扇区号来做两件事:一件是把起始扇区装入内存,另一件则是通过它找到FAT中的项,从而找到Loader占用的其余所有扇区。

在这里,我们把Loader装入内存的BaseOfLoader:OffsetOfLoader处

写一个函数来找到FAT中的项。函数的输入就是扇区号,输出则是其对应的FAT项的值

新增加了宏SectorNoOfFAT1,它与前面提到的RootDirSectors、SectorNoOfRootDirectory等宏一起,与FAT12有关的几个数字我们都定义成了宏,而不是在程序中进行计算。一方面,这是为缩小引导扇区代码考虑;

另一方面,这些数字一般情况下是不会变的,写代码计算它们其实是一种浪费。

由于一个FAT项可能跨越两个扇区,所以在代码中一次总是读两个扇区,以免在边界发生错误。

之后加载loader

新的宏DeltaSectorNo。根据下面的例子来看,文件RIVER.TXT对应的目录条目中的开始簇号是2。实际上,开始簇号是2对应的是数据区的第一个扇区。所以,我们需要有一个方法来计算簇号为X代表从引导扇区开始算起是第几个扇区。 根目录区占用RootDirSectors也即14个扇区,根目录区的开始扇区号是19,于是用“X+RootDirSectors+19-2”来算出“33”这个正确的扇区号。所以,我们又定义了一个宏DeltaSectorNo为17(即19-2)来帮助计算正确的扇区号: DeltaSectorNo equ 17

(6)向loader移交控制权

上面的代码调试通过后,我们就已经成功地将Loader加载入内存,下面让我们来一个跳转,开始执行Loader

(7)整理boot.asm 测试

为了在执行时实现更好的效果,增加如下代码

2首先清屏,然后显示字符串“Booting”。这样,加载Loader时打印的圆点也会出现在这个字符串的后面。 屏幕上的圆点数目表明我们读了几个扇区就把Loader加载完毕。

在加载完毕跳入loader前打印ready

更新引导扇区和loader。运行

修改bochsrc

运行,成功

(8)总结

Loader.bin本质上是个.COM文件,最大也不可能超过64KB。但是,我们已经成功突破512字节限制,这个进步无疑是巨大的。

Linux的的引导扇区代码Boot.s比我们的代码简单,它直接把内核移动到目标内存。我们的代码之所以复杂一些,是因为我们想和MSDOS的磁盘格式兼容,以便调试的时候容易一些。比如现在,我们就完全可以把第3章中的代码pmtest9.asm编译一下,将编译后的二进制命名为Loader.bin并复制到刚刚引导过 的软盘中覆盖掉原来简陋的Loader.bin。你会发现程序马上可以执行,结果如图所示。

现在的Loader仅仅是个Loader,它不是操作系统内核,也不能当做操作系统内核。我们希望自己的操作系统内核至少应该可以在Linux下用GCC编译链接,要不然,永远用汇编一点一点地写下去实在是太痛苦了。

那么,现在我们假设已经有了一个内核,Loader肯定要加载它入内存,而且内核开始执行的时候肯定已经在保护模式下了,所以,Loader要做的事情至少有两件:

加载内核入内存。 跳入保护模式。

 2.在软盘中找到指定的文件,读取其扇区信息

见1.(3)读软盘,根目录部分得loader起始扇区号

 3.将指定文件装入指定内存区,并执行

见1.(5)根据(3)读根目录得到的扇区号,读FAT将loader加载到内存

4.学会在bochs中使用xxd读取反汇编信息

见1.(4)写入boot.bin,loader.bin到软盘并反汇编调试

完成本次实验要思考的问题:

1.FAT12格式是怎样的?

FAT12 是DOS时代就开始使用的文件系统(File System),直到现在仍然在软盘上使用。

几乎所有的文件系统都会把磁盘划分为若干层次以方便组织和管理,这些层次包括:

扇区(Sector):磁盘上的最小数据单元。

簇(Cluster):一个或多个扇区。

分区(Partition):通常指整个文件系统。

引导扇区是整个软盘的第0个扇区,在这个扇区中有一个很重要的数据结构

叫做BPB(BIOS ParameterBlock),引导扇区的格式如表所示,其中名称以BPB_开头的域属于BPB,以BS_开头的域不属于BPB, 只是引导扇区(Boot Sector)的一部分。

紧接着引导扇区的是两个完全相同的FAT表,每个占用9个扇区。第二个FAT之后是根目录区的第一个扇区。根目录区的后面是数据区,如图所示。

要把Loader复制到软盘上并让引导扇区找到并加载它,来看一下引导扇区通过怎样的步骤才能找到文件,以及如何能够把文件内容全都读出来并放进内存里。 为简单起见,我们规定Loader只能放在根目录中,而根目录信息存放在FAT2后面的根目录区中。

根目录区。 根目录区位于第二个FAT表之后,开始的扇区号为19,它由若干个目录条目(Directory Entry)组成,条目最多有BPB_RootEntCnt个。由于根目录区的大小是依赖于BPB_RootEntCnt的,所以长度不固定。

根目录区中的每一个条目占用32字节,格式如表所示。

主要定义了文件的名称、属性、大小、日期以及在磁盘中的位置。

举例:

创建一个虚拟软盘,假设是x.img,把它作为FreeDos的B盘,格式化后就可以往其中添加文件和目录了(比如使用FreeDos 里的edit.exe)。这样,当我们想查看它的格式时,只需用二进制查看器打开x.img就可以。

通过FreeDos在这张虚拟软盘中添加以下几个文本文件:

RIVER.TXT,内容为riverriverriver。

FLOWER.TXT,内容为300个单词flower,用来测试文件跨越扇区的情况。(可以先建一个小文件,最后再把它改长,这 样可以让它对应的簇不连续,便于观察和理解。)

TREE.TXT,内容为treetreetree。

再添加一个HOUSE目录,然后在目录nHOUSE下添加两个文本文件:

179CAT.TXT,内容为catcatcat。

DOG.TXT,内容为dogdogdog。

由于根目录区从第19扇区开始,每个扇区512字节,所以其第一个字节位于偏移19*512=9728=0x2600处。用二 进制查看器来看看x.img的偏移0x2600处是什么,如下

以RIVER.TXT为例,它的各项值如表所示。

当我们寻找Loader时,只要发现文件名正确就认为它是我们要找的那一个文件。最后剩下最重要的信息DIR_FstClus,即文件开始簇号,它告诉我们文件存

放在磁盘的什么位置,从而让我们可以找到它。由于一簇只包含一个扇区,所以简化了计算过程,而且下文中说到“簇”的地方, 你也可以将它替换成“扇区”。

需要注意的是,数据区的第一个簇的簇号是2,而不是0或者1。

RIVER.TXT的开始簇号就是2,也就是说,此文件的数据开始于数据区第一个簇。

计算根目录区所占的扇区数:根目录区条目最多有BPB_RootEntCnt个,扇区数假设根目录区共占用RootDirSectors个扇区,则有:

之所以分子要加上(BPB_BytsPerSec—1),是为了保证此公式在根目录区无法填满整数个扇区时仍然成立。

在本例中,容易算出RootDirSectors=14。所以:数据区开始扇区号=根目录区开始扇区号+14=19+14=33

第33扇区的偏移量是0x4200(512×33),让我们看一下这里的内容,如下:

对于小于512字节的文件来说,FAT表用处不大,但如果文件大于512字节,我们需要FAT表来找到所有的簇(扇区)。FAT表有两个,FAT2可看做是FAT1的备份,它们通常是一样的。FAT1的开始扇区号是1,偏移为512字节(0x200),如下:

每12位称为一个FAT项(FATEntry),代表一个簇。第 0个和第1个FAT项始终不使用,从第2个FAT项开始表示数据区的每一个簇,也就是说,第2个FAT项表示数据区第一个簇,依此类推。前文说过,数据区的第一个簇的簇号是2,和这里是相呼应的。

由于每个FAT项占12位,包含一个字节和另一个字节的一半。假设连续3个字节分别如图所示,那么灰色框表示的是前一个FAT项(FATEntry1),BYTE1是FATEntry1的低8位,BYTE2的低4位是 FATEntry1的高4位;白色框表示的是后一个FAT项(FATEntry2),BYTE2的高4位是FATEntry2的低4位,BYTE3是FATEntry2的高8 位。

通常,FAT项的值代表的是文件下一个簇号,但如果值大于或等于0xFF8,则表示当前簇已经是本文件的最后一个簇。如果值为0xFF7,表示它是一个坏簇。

文件RIVER.TXT的开始簇号是2,对应FAT表中的值为0xFFF,表示这个簇已经是最后一个。 (FF 8F 00 注意是低地址,取ff和8F中的F)

我们来看一个长一点的文件FLOWER.TXT,它的DIR_FstClus值为3,对应第3个FAT项。结合我们打印出的FAT表内容我们知道,此FAT项值为0x008,也就是说,这个簇不是文件的最后一个簇,下一个簇号为8。我们再找到第8个FAT项,发现值为0x009, 接下来第9个FAT项值为0x00A,第0xA个FAT项值为0xFFF。所以,FLOWER.TXT占用了第3、8、9、10,共计4个簇。

这里需要注意一点,一个FAT项可能会跨越两个扇区,这种情况在编码实现的过程中要考虑在内。

2.如何读取一张软盘的信息

使用bios中断 int 13h

3.如何在软盘中找到指定的文件

先根据文件名遍历根目录区,找到起始扇区,然后根据起始扇区号查找对应FAT项

4.如何在系统引导过程中,从读取并加载一个可执行文件

到内存,并转交控制权?

在引导扇区boot sector中编写函数,实现先根据文件名遍历根目录区,查找文件起始扇区号,然后根据起始扇区号找到对应起始FAT项,接着根据FAT项加载对应扇区到内存,直到加载完毕。(FAT值为FFF)

之后跳转到加载内存的起始位置,开始执行,就移交了控制权。

5.为什么需要这个Loader程序不包含dos系统调用?

因为要用loader加载内核,跳入保护模式。

Loader调用的是bios的中断。Dos是操作系统,要用loader装入,装入前无法调用。

oslab oranges 一个操作系统的实现 实验五 让操作系统走进保护模式的更多相关文章

  1. oslab oranges 一个操作系统的实现 实验四 认识保护模式(三):中断异常

    实验目的: 理解中断与异常机制的实现机理 对应章节:第三章3.4节,3.5节 实验内容: 1. 理解中断与异常的机制 2. 调试8259A的编程基本例程 3. 调试时钟中断例程 4. 建立IDT,实现 ...

  2. oslab oranges 一个操作系统的实现 实验二 认识保护模式

    https://github.com/yyu/osfs00 实验目的: 理解x86架构下的段式内存管理 掌握实模式和保护模式下段式寻址的组织方式. 关键数据结构.代码组织方式 掌握实模式与保护模式的切 ...

  3. oslab oranges 一个操作系统的实现 实验三 认识保护模式(二):分页

    实验目的: 掌握内存分页机制 对应章节:3.3 实验内容: 1.认真阅读章节资料,掌握什么是分页机制 2. 调试代码,掌握分页机制基本方法与思路 – 代码3.22中,212行---237行,设置断点调 ...

  4. oslab oranges 一个操作系统的实现 实验一

    实验目的: 搭建基本实验环境,熟悉基本开发与调试工具 对应章节:第一.二章 实验内容: 1.认真阅读章节资料 2.在实验机上安装virtualbox,并安装ubuntu 3.安装ubuntu开发环境, ...

  5. oslab oranges 一个操作系统的实现 final

    见 github  https://github.com/TouwaErioH/subjects/tree/master/oslab-oranges

  6. 《Orange’s 一个操作系统的实现》1.搭建操作系统开发环境

    书中给出了两种环境:windows和linux,平台选择根据自己喜好.本人这里选择ubuntu10.04+virtualbox作为开发平台. 1.下载.安装VirtualBox     http:// ...

  7. 20155222卢梓杰 实验五 MSF基础应用

    实验五 MSF基础应用 1.一个主动攻击实践,如ms17_010_eternalblue漏洞; 本次攻击目标是win7虚拟机 首先进行相应配置 然后点launch 就成功了 针对win7的漏洞还是相对 ...

  8. 20155229——实验五《 Java网络编程及安全》

    20155229--实验五 Java网络编程及安全 实验内容 实验一: 两人一组结对编程: 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA ...

  9. 《一个操作系统的实现》 ubuntu系统环境配置

    <一个操作系统的实现> ubuntu系统环境配置 电脑之前已经安装了gcc. 一.nasm安装:sudo apt-get install nasm或官网下载http://sourcefor ...

随机推荐

  1. BAPI_PO_CHANGE

    这两天用BAPI更改采购订单,遇到了一些问题,最后调试解决了.记录如下吧.要修改的是采购订单的物料号和批次,在网上看到其它人写过关于 BAPI_PO_CHANGE的用法,但是具体问题还要具体分析啊. ...

  2. Centos7下安装MySQL8.0.23-小白的开始

    首先简单介绍一下什么叫MySQL: 数据库简而言之就是存储数据的仓库,为了方便数据的存储和管理,它将数据按照特定的规律存储在磁盘上.是为了实现一定的目的,按照某种规则组织起来的数据的集合: MySQL ...

  3. connection-backoff ConnectionBackoff Strategy 回退

    grpc/connection-backoff.md at master · grpc/grpc https://github.com/grpc/grpc/blob/master/doc/connec ...

  4. 【汇编实践】go assembly

    https://mp.weixin.qq.com/s/B577CdUkWCp_XgUc1VVvSQ asmshare/layout.md at master · cch123/asmshare htt ...

  5. (Oracle)误删oracle表结构恢复

    在操作数据库时,我们常常会不小心把表结构删除了.有时候建表很麻烦大到100多个字段,而又找不到当初的建表语句.其实这时候不用担心,oracle和咱们widows一样,他也有个回收站,只要你没有清除回收 ...

  6. CSS补充2

    浮动是css里面布局最多的一个属性效果:两个元素并排了,并且两个元素都能够设置宽度和高度 四个特性: 1.浮动的元素脱标 2.浮动的元素互相贴靠 3.浮动的元素有"字围"效果 4. ...

  7. 题解【CF1444A Division】

    题面 t 组数据. 给定参数 p,q,求一个最大的 x,满足 \((x|p)∧(q∤x)\). \(1\le t \le 500\),\(1\le p \le10^{18}\),\(2\le q\le ...

  8. Linux环境Hive安装配置及使用

    Linux环境Hive安装配置及使用 一.Hive Hive环境前提 二.Hive架构原理解析 三.Hive-1.2.2单机安装流程 (1) 解压apache-hive-1.2.2-bin.tar.g ...

  9. mysql高级day2

    Mysql高级-day02 1. Mysql的体系结构概览 整个MySQL Server由以下组成 Connection Pool : 连接池组件 Management Services & ...

  10. B - How Many Tables (多少桌)

    题目大致意思: 有n个人在一起吃饭,有些人互相认识.认识的人想坐在一起,不想跟陌生人坐.例如A认识B,B认识C,那么A.B.C会坐在一张桌子上. 给出认识的人,问需要多少张桌子 Today is Ig ...