os引导程序boot从扇区拷贝os加载程序loader文件到内存(boot copy kernel to mem in the same method)
【0】README
- 0.1) 本代碼旨在演示 在boot 代碼中,如何 通過 loader文件所在根目錄條目 找出該文件的 在 軟盤所有全局扇區(qū)號(hào)(簇號(hào)),并執(zhí)行內(nèi)存中的 loader 代碼;
- 0.2) 此代碼非常重要,關(guān)系到bootloader的加載和運(yùn)行(打印字母 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 ,”從扇區(qū)copy os加載程序 loader 到內(nèi)存0x09000:0100“這個(gè)任務(wù) 是在 引導(dǎo)扇區(qū)中的引導(dǎo)程序boot 中完成的,而且在 boot 完成本任務(wù)之前,引導(dǎo)程序boot還完成 ”在根目錄區(qū)尋找 加載程序loader 對(duì)應(yīng)的根目錄條目“的任務(wù),注意這與linux 是不同的;
- 0.5)即是說,orange’s os 中的啟動(dòng)程序boot 執(zhí)行了兩個(gè)任務(wù): os引導(dǎo)程序boot 在根目錄區(qū)尋找os加載程序文件loader 對(duì)應(yīng)的根目錄條目 + os引導(dǎo)程序boot 從扇區(qū)拷貝os加載程序loader文件到內(nèi)存(boot copy kernel to mem in the same method)
- Attention)
- A1) loader文件所在根目錄條目 是由 p109.asm 得到的, 在查找完文件名稱且匹配后,di 指向文件名的后一個(gè)字節(jié),但di還存在于當(dāng)前匹配成功的 根目錄條目中;
- A2) 要死死記住 根目錄條目的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)的是:(文件名 + 文件屬性 + 最后一次寫入時(shí)間 + 最后一次寫入日期 + 此條目對(duì)應(yīng)的開始簇號(hào) + 文件大小)
- A2) 要死死記住 根目錄條目的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)的是:(文件名 + 文件屬性 + 最后一次寫入時(shí)間 + 最后一次寫入日期 + 此條目對(duì)應(yīng)的開始簇號(hào) + 文件大小)
【1】SOURCE CODE
LABEL_FILENAME_FOUND: ; 找到 LOADER.BIN 后便來到這里繼續(xù)
mov ax, RootDirSectors ; RootDirSectors=14and di, 0FFE0h ; di -> 當(dāng)前條目的開始,每個(gè)條目=32字節(jié),0ffe0==1111 1111 1110 0000add di, 01Ah ; di -> 首 Sector ,條目數(shù)據(jù)結(jié)構(gòu)中,開始簇號(hào)的offset=26字節(jié);mov cx, word [es:di]push cx ; 保存此 Sector 在 FAT 中的序號(hào) ,cx等于loader文件的開始簇號(hào)(在數(shù)據(jù)區(qū))add cx, axadd cx, DeltaSectorNo ; cl <- LOADER.BIN的起始扇區(qū)號(hào)(0-based); 這里加完之后,cx=該loader 文件相對(duì)于0號(hào)扇區(qū)的扇區(qū)號(hào)(也即相對(duì)于整個(gè)軟盤而言)mov ax, BaseOfLoadermov es, ax ; es <- BaseOfLoadermov bx, OffsetOfLoader ; bx <- OffsetOfLoadermov ax, cx ; ax <- Sector 號(hào)LABEL_GOON_LOADING_FILE:push ax ; `.push bx ; | es:bp 是串地址,CX=串長(zhǎng)度,ah= ,al= '.' 打印什么東西, bh=頁號(hào),bl=黑底紅字mov ah, 0Eh ; | 每讀一個(gè)扇區(qū)就在 "Booting " 后面mov al, '.' ; | 打一個(gè)點(diǎn), 形成這樣的效果:mov bl, 0Fh ; | Booting ...... int 10h ; | | 一句話說完,以上對(duì)register的初始化,都是為觸發(fā)10h 號(hào)中斷做準(zhǔn)備工作的, pop bx ; |pop ax ; /mov cl, 1call ReadSector ; 這個(gè)ReadSector非常重要,目的就是讀取cl個(gè)扇區(qū)到 es:bx中,而bx 每次都自增512字節(jié);(這是讀軟盤某扇區(qū)到內(nèi)存中的關(guān)鍵步驟)
pop ax ; cur_line=154, 取出此 Sector 在 FAT 中的序號(hào)(ax <- cx),line133 push cx 已壓入棧, cx=Loader文件在數(shù)據(jù)區(qū)的開始簇號(hào);call GetFATEntry ; 找到序號(hào)為 ax 的 Sector 在 FAT 中的條目, 結(jié)果放在 ax 中
cmp ax, 0FFFhjz LABEL_FILE_LOADED ; 相等,則說明 該簇號(hào)是最后一個(gè)簇號(hào)push ax ; 保存 Sector 在 FAT 中的序號(hào)mov dx, RootDirSectorsadd ax, dxadd ax, DeltaSectorNo ; DeltaSectorNo equ 17add bx, [BPB_BytsPerSec] ; bx加上一個(gè)扇區(qū)的字節(jié)數(shù),即跳轉(zhuǎn)到下一個(gè)扇區(qū);因?yàn)樽x取地址是 es:bxjmp LABEL_GOON_LOADING_FILE LABEL_FILE_LOADED:mov dh, 1 ; "Ready." 加載完畢 call DispStr ; 顯示字符串; jmp BaseOfLoader:OffsetOfLoader; 這一句正式跳轉(zhuǎn)到已加載到內(nèi), 開始執(zhí)行l(wèi)oader 代碼;
; 存中的 LOADER.BIN 的開始處,
; 開始執(zhí)行 LOADER.BIN 的代碼。
; Boot Sector 的使命到此結(jié)束**
GetFATEntry Source Code
;—————————————————————————-
; 函數(shù)名: GetFATEntry
;—————————————————————————-
; 作用:
; 找到序號(hào)為 ax 的 Sector 在 FAT 中的條目, 結(jié)果放在 ax 中
; 需要注意的是, 中間需要讀 FAT 的扇區(qū)到 es:bx 處, 所以函數(shù)一開始保存了 es 和 bx
【2】Conclusion(本代碼繼p109 后,接著擺)
2.0)寫在前面:假設(shè)這里有個(gè)內(nèi)核,loader加載該內(nèi)核到內(nèi)存,而且內(nèi)核開始執(zhí)行的時(shí)候肯定已經(jīng)在保護(hù)模式下了。所以loader需要做的事情有兩件:
- 2.0.a)加載內(nèi)核入內(nèi)存;
- 2.0.b)跳入保護(hù)模式;
2.1)下面演示 以上代碼的執(zhí)行步驟:
step1)計(jì)算該文件起始簇號(hào)對(duì)應(yīng)的全局扇區(qū)號(hào):
從根目錄條目中抽取出 該文件的起始簇號(hào)(FAT專門用于存儲(chǔ)文件在數(shù)據(jù)區(qū)的簇號(hào),簇號(hào)等于一個(gè)或多個(gè)扇區(qū)),該簇號(hào)是相對(duì)于數(shù)據(jù)區(qū)的簇號(hào),(因?yàn)楸綟AT12文件系統(tǒng)中,一個(gè)簇號(hào)==一個(gè)扇區(qū),所以簇號(hào)就等于扇區(qū)的說法,但是不管怎么,知道簇號(hào),我就可以知道扇區(qū)號(hào),這些設(shè)置是在 FAT12 的引導(dǎo)扇區(qū)中定義好了的)所以,我們要算出該簇號(hào)對(duì)應(yīng)的 全局簇號(hào)(扇區(qū)號(hào)),因?yàn)榈谝粋€(gè)和第2個(gè)FAT不使用,所以全局簇號(hào)最后還要減2。
step2)從軟盤上 讀取該扇區(qū)到內(nèi)存地址 es:bx=9000:1000處,以便進(jìn)行數(shù)據(jù)分析;(當(dāng)然,每次循環(huán)后,偏移地址要增加512字節(jié),也即連續(xù)讀取該文件的扇區(qū)內(nèi)容到 起始位置0x9000:1000處);
step3)找出 step1 中算出扇區(qū)號(hào)在 FAT中的條目:- step3.1)首先,算出該扇區(qū)在FAT的 對(duì)應(yīng)條目 FATentry 在 FAT中的偏移量(一個(gè)條目= 12bits = 1.5Bytes);(注意,這里的條目是FAT條目,不是根目錄區(qū)條目,不要搞混了)
- step3.2)然后算出該偏移量在哪個(gè)扇區(qū),以及在該扇區(qū)的偏移量;
- step3.3)讀取對(duì)應(yīng)FAT條目所在扇區(qū)和相鄰扇區(qū)(也即讀2個(gè)扇區(qū),因?yàn)闂l目占1.5個(gè)字節(jié),可能跨扇區(qū)存儲(chǔ))到 es:bx= (9000h-100h): 00 處;
- step3.4)在es:bx處 對(duì)兩個(gè)扇區(qū)求出 該扇區(qū)對(duì)應(yīng)的 FAT條目值(即12位值);
step4)比較FAT條目值 是否 == fffh:
- step4.1)如果相等,則證明該條目是最后一個(gè)條目,即over了;
- step4.2)如果不等,將該FAT條目壓棧(因?yàn)樗且粋€(gè)鏈條,不斷壓棧,直到 fffh 出現(xiàn)),然后更新該文件的下一個(gè)全局扇區(qū)號(hào)(ax+RootDirSectors+DeltaSectorNo),然后再代入step2)進(jìn)行循環(huán);最后就可以找出該文件所對(duì)應(yīng)的所有FAT條目,即該文件所占用的所有簇,也即所有扇區(qū)了,即是是壓棧形成的那個(gè)鏈條值;
step5)找到該文件占用的所有扇區(qū)后,跳轉(zhuǎn)到es:bx的地址,即 jmp BaseOfLoader:OffsetOfLoader ,該 loader文件 僅僅是 打印了 字符 ‘L’ ,(該文件內(nèi)容,就是剛才step2步驟中把軟盤扇區(qū)中的內(nèi)容讀入內(nèi)存 es:bx 的內(nèi)容),(因?yàn)檎鐂tep2所說的那樣,在上述循環(huán)過程中,程序已經(jīng)把該文件的所有扇區(qū)內(nèi)容讀取到起始地址為 BaseOfLoader:OffsetOfLoader=0x90000:1000 的內(nèi)容空間中去了)
jmp BaseOfLoader:OffsetOfLoader ; 這一句正式跳轉(zhuǎn)到已加載到內(nèi) ; 存中的 LOADER.BIN 的開始處, ; 開始執(zhí)行 LOADER.BIN 的代碼。 ; Boot Sector 的使命到此結(jié)束
Attntion)其實(shí)你發(fā)現(xiàn): 這個(gè) BaseOfLoader : OffsetOfLoader 處的內(nèi)容,是 LABEL_GOON_LOADING_FILE 標(biāo)識(shí)符后的 ReadSector函數(shù)每次讀一個(gè)扇區(qū)讀進(jìn)去的,而每次的起始扇區(qū)號(hào)由 GetFATEntry 函數(shù) 提供的,而 GetFATEntry 函數(shù)的作用:找到序號(hào)為 ax 的 Sector 在 FAT 中的條目, 結(jié)果放在 ax 中。就這樣 ReadSector函數(shù) + GetFATEntry 函數(shù)配合起來就把loader文件從 軟盤的扇區(qū)讀到了 起始內(nèi)存地址 es:bx=9000h:0100 (每次循環(huán)后,偏移地址自增512字節(jié));
Complementary)FAT的作用:當(dāng)文件size 大于 512B,則FAT是找出該文件所占用的全部簇(簇:一個(gè)或多個(gè)扇區(qū),引導(dǎo)扇區(qū)的BPB_SecPerClus記錄該數(shù)字)
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的os引导程序boot从扇区拷贝os加载程序loader文件到内存(boot copy kernel to mem in the same method)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FAT12中,如何定位大于一个扇区(51
- 下一篇: linux内核与用户态交互(linux内