【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. Current Sourcing (拉電流) and Current Sinking(灌電流)

    Current Sourcing and Sinking Current sourcing and sinking is often mentioned in relation to electron ...

  2. python - break和continue

    break 终止整个循环:当循环或判断执行到break语句时,即使判断条件为True或者序列尚未完全被历遍,都会跳出循环或判断 for i in xrange(10): print(i) if i = ...

  3. [转]谈谈Java中的语法糖

    *该博客转自 http://blog.csdn.net/danchu/article/details/54986442 语法糖(Syntactic Sugar),也称糖衣语法,指在计算机语言中添加的某 ...

  4. Fio IO性能测试

    fio-2.1.2-1.el5.rf.x86_64 介绍 fio different types of I/O engines (sync, mmap, libaio, posixaio, SG v3 ...

  5. A Wasserstein Distance[贪心/模拟]

    链接:https://www.nowcoder.com/acm/contest/91/A来源:牛客网 最近对抗生成网络(GAN)很火,其中有一种变体WGAN,引入了一种新的距离来提高生成图片的质量.这 ...

  6. JMeter特殊情况二:针对某些请求数据每次请求都是变化的情况

    概要:某些post请求,例如,登录的请求除了有我们再页面上需要输入的一些值(用户名.密码.是否记住密码等)之外,还有其他的参数,例如token等等,而且这些参数不固定,也就是说每一次post请求这些参 ...

  7. Decrease (Judge ver.)

    题目描述 We have a sequence of length N consisting of non-negative integers. Consider performing the fol ...

  8. SQLite复杂表的更新方式

    SQLite复杂表的更新方式   在SQLite中,如果早期设计的表无法满足需要,就需要对表进行更新,如修改名字.添加列.如果针对简单表,修改起来相对容易,直接使用提供的ALTER命令即可.但是如果该 ...

  9. Light oj 1125 - Divisible Group Sums (dp)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1125 题意: 给你n个数,q次询问,每次询问问你取其中m个数是d的整数倍的方案 ...

  10. 让cpu占用率曲线听你指挥(多核处理器)

    编程之美 1.1 让cpu占用率曲线听你指挥(多核处理器) [版权声明]转载请注明出处 http://www.cnblogs.com/TenosDoIt/p/3242910.html  [目录] 不考 ...