【0】README

  • 0.1) 本代码旨在演示 在boot 代码中,如何 通过 loader文件所在根目录条目 找出该文件的 在 软盘所有全局扇区号(簇号),并执行内存中的 loader 代码;
  • 0.2) 此代码非常重要,关系到bootloader的加载和运行(打印字母 L)(干货)
  • 0.3) source code from orange’s implemention of a os and for complete code , please visit https://github.com/pacosonTang/Orange-s-OS/blob/master/boot.asm
  • 0.4) 就本os而言,即orange’s os ,”从扇区copy os加载程序 loader 到内存0x09000:0100“这个任务 是在 引导扇区中的引导程序boot 中完成的,而且在 boot 完成本任务之前,引导程序boot还完成 ”在根目录区寻找 加载程序loader 对应的根目录条目“的任务,注意这与linux 是不同的;
  • 0.5)即是说,orange’s os 中的启动程序boot 执行了两个任务: os引导程序boot 在根目录区寻找os加载程序文件loader 对应的根目录条目 + os引导程序boot 从扇区拷贝os加载程序loader文件到内存(boot copy kernel to mem in the same method)
  • Attention)
    • A1) loader文件所在根目录条目 是由 p109.asm 得到的, 在查找完文件名称且匹配后,di 指向文件名的后一个字节,但di还存在于当前匹配成功的 根目录条目中;
    • A2) 要死死记住 根目录条目的数据结构存储的是:(文件名 + 文件属性 + 最后一次写入时间 + 最后一次写入日期 + 此条目对应的开始簇号 + 文件大小)


【1】SOURCE CODE

LABEL_FILENAME_FOUND: ; 找到 LOADER.BIN 后便来到这里继续

 mov    ax, RootDirSectors ; RootDirSectors=14
and di, 0FFE0h ; di -> 当前条目的开始,每个条目=32字节,0ffe0==1111 1111 1110 0000
add di, 01Ah ; di -> 首 Sector ,条目数据结构中,开始簇号的offset=26字节;
mov cx, word [es:di]
push cx ; 保存此 Sector 在 FAT 中的序号 ,cx等于loader文件的开始簇号(在数据区)
add cx, ax
add cx, DeltaSectorNo ; cl <- LOADER.BIN的起始扇区号(0-based)
; 这里加完之后,cx=该loader 文件相对于0号扇区的扇区号(也即相对于整个软盘而言)
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader
mov ax, cx ; ax <- Sector 号 LABEL_GOON_LOADING_FILE:
push ax ; `.
push bx ; | es:bp 是串地址,CX=串长度,ah= ,al= '.' 打印什么东西, bh=页号,bl=黑底红字
mov ah, 0Eh ; | 每读一个扇区就在 "Booting " 后面
mov al, '.' ; | 打一个点, 形成这样的效果:
mov bl, 0Fh ; | Booting ......
int 10h ; | | 一句话说完,以上对register的初始化,都是为触发10h 号中断做准备工作的,
pop bx ; |
pop ax ; / mov cl, 1

call ReadSector ; 这个ReadSector非常重要,目的就是读取cl个扇区到 es:bx中,而bx 每次都自增512字节;(这是读软盘某扇区到内存中的关键步骤)

 pop    ax  ; cur_line=154, 取出此 Sector 在 FAT 中的序号(ax <- cx),line133 push cx 已压入栈, cx=Loader文件在数据区的开始簇号;

call GetFATEntry ; 找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中

 cmp    ax, 0FFFh
jz LABEL_FILE_LOADED ; 相等,则说明 该簇号是最后一个簇号
push ax ; 保存 Sector 在 FAT 中的序号
mov dx, RootDirSectors
add ax, dx
add ax, DeltaSectorNo ; DeltaSectorNo equ 17
add bx, [BPB_BytsPerSec] ; bx加上一个扇区的字节数,即跳转到下一个扇区;因为读取地址是 es:bx
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED: mov dh, 1 ; "Ready." 加载完毕
call DispStr ; 显示字符串 ;

jmp BaseOfLoader:OffsetOfLoader; 这一句正式跳转到已加载到内, 开始执行loader 代码;

; 存中的 LOADER.BIN 的开始处,

; 开始执行 LOADER.BIN 的代码。

; Boot Sector 的使命到此结束**


GetFATEntry Source Code

;—————————————————————————-

; 函数名: GetFATEntry

;—————————————————————————-

; 作用:

; 找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中

; 需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx

GetFATEntry:
push es
push bx
push ax ; cur_line=269, ax 存储该文件在FAT中的开始簇号(又FAT的第0和第1项不使用,所以FAT条目的开始簇号起步价为2,且数据区的第一个簇的簇号是2)
mov ax, BaseOfLoader; `.
sub ax, 0100h ; | 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT
; | 因为每个扇区=512B,FAT=8个扇区=4k;
mov es, ax ; /
pop ax ; ax恢复line269 push ax 的值,(current line=273)
mov byte [bOdd], 0 ; bOdd db 0 ; 奇数(value=1)还是偶数(value=0)
mov bx, 3
mul bx ; dx:ax = ax * 3
mov bx, 2
div bx ; dx:ax / 2 ==> ax <- 商, dx <- 余数
cmp dx, 0
jz LABEL_EVEN
mov byte [bOdd], 1 ; 奇数(value=1)偶数(value=0)
LABEL_EVEN:;偶数
; 现在 ax 是 FATEntry 在 FAT 中的偏移量,下面来
; 计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
; 因为 FATEntry 占用12位,解释了line276 line278 的 乘3 和 除2 操作;cur_line=285
xor dx, dx
mov bx, [BPB_BytsPerSec] ;
div bx ; dx:ax / BPB_BytsPerSec
; ax <- 商 (FATEntry 所在的扇区相对于 FAT 的扇区号 , FAT共有9个扇区 )
; dx <- 余数 (FATEntry 在扇区内的偏移)
push dx ; FATEntry 在扇区内的偏移
mov bx, 0 ; bx <- 0 于是, es:bx = (BaseOfLoader - 100):00
add ax, SectorNoOfFAT1 ; 此句之后的 ax 就是 FATEntry 所在的扇区号(全局)
; SectorNoOfFAT1 equ 1 ; FAT1 的第一个扇区号 = BPB_RsvdSecCnt
mov cl, 2
call ReadSector ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界
; 发生错误, 因为一个 FATEntry 可能跨越两个扇区
; ReadSector 从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中, es:bx = (BaseOfLoader - 100):00
pop dx ; 将 FATEntry 在扇区内的偏移(line292) 出栈,cur_line=300
add bx, dx ; 因为bx=0,偏移量 -> bx
mov ax, [es:bx] ; es:bx = (BaseOfLoader - 100):00+dx( FATEntry 在扇区内的偏移)
cmp byte [bOdd], 1 ; 奇数(value=1)偶数(value=0)
jnz LABEL_EVEN_2 ; 偶数
shr ax, 4 ; 注意,FATEntry=12bit,占用1.5Byte, 这里很好理解
LABEL_EVEN_2:
and ax, 0FFFh LABEL_GET_FAT_ENRY_OK: pop bx
pop es
ret

【2】Conclusion(本代码继p109 后,接着摆)

  • 2.0)写在前面:假设这里有个内核,loader加载该内核到内存,而且内核开始执行的时候肯定已经在保护模式下了。所以loader需要做的事情有两件:

    • 2.0.a)加载内核入内存;
    • 2.0.b)跳入保护模式;
  • 2.1)下面演示 以上代码的执行步骤:

    step1)计算该文件起始簇号对应的全局扇区号:

    从根目录条目中抽取出 该文件的起始簇号(FAT专门用于存储文件在数据区的簇号,簇号等于一个或多个扇区),该簇号是相对于数据区的簇号,(因为本FAT12文件系统中,一个簇号==一个扇区,所以簇号就等于扇区的说法,但是不管怎么,知道簇号,我就可以知道扇区号,这些设置是在 FAT12 的引导扇区中定义好了的)所以,我们要算出该簇号对应的 全局簇号(扇区号),因为第一个和第2个FAT不使用,所以全局簇号最后还要减2。

    step2)从软盘上 读取该扇区到内存地址 es:bx=9000:1000处,以便进行数据分析;(当然,每次循环后,偏移地址要增加512字节,也即连续读取该文件的扇区内容到 起始位置0x9000:1000处);

    step3)找出 step1 中算出扇区号在 FAT中的条目:

    • step3.1)首先,算出该扇区在FAT的 对应条目 FATentry 在 FAT中的偏移量(一个条目= 12bits = 1.5Bytes);(注意,这里的条目是FAT条目,不是根目录区条目,不要搞混了)
    • step3.2)然后算出该偏移量在哪个扇区,以及在该扇区的偏移量;
    • step3.3)读取对应FAT条目所在扇区和相邻扇区(也即读2个扇区,因为条目占1.5个字节,可能跨扇区存储)到 es:bx= (9000h-100h): 00 处;
    • step3.4)在es:bx处 对两个扇区求出 该扇区对应的 FAT条目值(即12位值);

    step4)比较FAT条目值 是否 == fffh:

    • step4.1)如果相等,则证明该条目是最后一个条目,即over了;
    • step4.2)如果不等,将该FAT条目压栈(因为它是一个链条,不断压栈,直到 fffh 出现),然后更新该文件的下一个全局扇区号(ax+RootDirSectors+DeltaSectorNo),然后再代入step2)进行循环;最后就可以找出该文件所对应的所有FAT条目,即该文件所占用的所有簇,也即所有扇区了,即是是压栈形成的那个链条值;

    step5)找到该文件占用的所有扇区后,跳转到es:bx的地址,即 jmp BaseOfLoader:OffsetOfLoader ,该 loader文件 仅仅是 打印了 字符 ‘L’ ,(该文件内容,就是刚才step2步骤中把软盘扇区中的内容读入内存 es:bx 的内容),(因为正如step2所说的那样,在上述循环过程中,程序已经把该文件的所有扇区内容读取到起始地址为 BaseOfLoader:OffsetOfLoader=0x90000:1000 的内容空间中去了)


jmp BaseOfLoader:OffsetOfLoader ; 这一句正式跳转到已加载到内
; 存中的 LOADER.BIN 的开始处,
; 开始执行 LOADER.BIN 的代码。
; Boot Sector 的使命到此结束

Attntion)其实你发现: 这个 BaseOfLoader : OffsetOfLoader 处的内容,是 LABEL_GOON_LOADING_FILE 标识符后的 ReadSector函数每次读一个扇区读进去的,而每次的起始扇区号由 GetFATEntry 函数 提供的,而 GetFATEntry 函数的作用:找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中。就这样 ReadSector函数 + GetFATEntry 函数配合起来就把loader文件从 软盘的扇区读到了 起始内存地址 es:bx=9000h:0100 (每次循环后,偏移地址自增512字节);

Complementary)FAT的作用:当文件size 大于 512B,则FAT是找出该文件所占用的全部簇(簇:一个或多个扇区,引导扇区的BPB_SecPerClus记录该数字)

版权声明:本文为博主原创文章,未经博主允许不得转载。

os引导程序boot从扇区拷贝os加载程序loader文件到内存(boot copy kernel to mem in the same method)的更多相关文章

  1. (转)spring boot实战(第六篇)加载application资源文件源码分析

    原文:http://blog.csdn.net/liaokailin/article/details/48878447

  2. 启动django时报错Watching for file changes with StatReloader(使用状态加载程序监视文件更改 )

    原因:可能是Django版本和Python版本或者PyMysql版本不一致 解决:升级或者降级Django版本 命令如下: pip install django==2.1.7 #django==版本号 ...

  3. os引导程序boot 在根目录区寻找os加载程序文件loader 对应的根目录条目

    [0]README 0.0) source code from orange's implemention of a os and for complete code , please visit h ...

  4. CAD调试时抛出“正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码”异常的解决方法

    这些天重装了电脑Win10系统,安装了CAD2012和VS2012,准备进行软件开发.在调试程序的时候,CAD没有进入界面就抛出 “正试图在 os 加载程序锁内执行托管代码.不要尝试在 DllMain ...

  5. 正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样...

    出错提示: 正尝试在 OS 加载程序锁内执行托管代码.不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起. 原因分析: .NET2.0中增加了42种非常强大的调试助手 ...

  6. 检测到 LoaderLock:DLL"XXXX"正试图在OS加载程序锁内执行

    解决方法: ctrl+D+E或alt+ctl+e或使用菜单调试——>异常——>异常窗口——>Managed Debugging Assistants——>去掉LoaderLoc ...

  7. 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码

    来自:http://www.cnblogs.com/lcxu2/archive/2011/01/16/2004016.html 正试图在 os 加载程序锁内执行托管代码.不要尝试在 DllMain 或 ...

  8. 在AE二次开发中出“正试图在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。”异常解决方案

    今天的一个项目总用到了AE的开发组件,也就是ESRI公司提供的一系列的开发包(组件)都是以dll(动态链接库的形式)然后今天在调试的时候却出现了“正试图在 OS 加载程序锁内执行托管代码.不要尝试在 ...

  9. 正试图在 os 加载程序锁内执行托管代码

    正试图在 os 加载程序锁内执行托管代码.不要尝试在 DllMain 或映像初始化函数内运行托管代码... 当我在窗体初始化的时候,调用了一个外部的dill时,它就不知什么原因的 抛出一个“正试图在 ...

随机推荐

  1. 【MFC】Tab Control 控件的使用(转)

    原文转自 http://blog.csdn.net/hustspy1990/article/details/5425365 1.先建立一个对话框MFC应用程序,然后在工具箱里面把Tab Control ...

  2. android的布局-----TableLayout(表格布局)

    学习导图 (1)TableLayout的相关简介 java的swing编程和html中经常会使用到表格,可见表格的应用开发中使用还是比较多的,同样android也为我们提供这样的布局方式. (2)如何 ...

  3. DNS解析过程详解【转】

    转自:http://blog.chinaunix.net/uid-28216282-id-3757849.html 先说一下DNS的几个基本概念: 一. 根域 就是所谓的“.”,其实我们的网址www. ...

  4. Python Challenge 第十三关

    第13关.一张电话的图片,一句话:phone that evil.看到电话,加上之前关卡有些图片有链接,我就在电话按键上都点点试试,果然 5 是个链接,就点了进去.出来一个XML文件,第一句写着:Th ...

  5. JAVA中如何创建一个二维数组,然后给二维数组赋值!

    普通的赋值是:int[][] i = {{1,2,3},{2,3,4},{1,3,4}}; 如果是其他情况可以这样:比如: import java.util.* public class TT(){ ...

  6. SpringMVC+Shiro权限管理(转载)

    源码 http://pan.baidu.com/s/1pJzG4t1 SpringMVC+Shiro权限管理 博文目录 权限的简单描述 实例表结构及内容及POJO Shiro-pom.xml Shir ...

  7. NBNS扫描工具nbtscan-unixwiz

    NBNS扫描工具nbtscan-unixwiz NBNS是NetBIOS Name Service的缩写,表示NetBIOS名称解析服务.NETBIOS是一种网络协议,用于实现消息通信和资源共享.利用 ...

  8. First Bad Version - LeetCode

    You are a product manager and currently leading a team to develop a new product. Unfortunately, the ...

  9. MySQL备份工具收集

    说明:MySQL的备份不像SQL Server那么的简单,备份时需要分数据库引擎类型,现在主流的就两个:InnoDB和MyISAM,而这两种类型备份方式各不一样. MyISAM: mysqlhotco ...

  10. 【java】Map、Set、List不同数据结构的各种不同循环迭代的效率对比,使用场景

    Map.Set.List不同数据结构的各种不同循环迭代的效率对比,使用场景 引申一个地址:Map迭代的使用keySet和entitySet的效率