Linux-0.01 引導程式碼分析-boot.s

來源:互聯網
上載者:User

Linux-0.01 的引導部分主要由兩個原始碼完成:boot.s 與 head.s 。boot.s 由 BIOS 載入執行,head.s 是 32 位的引導代碼,在最後會調用 main() 函數,完成系統的引導。

boot.s 代碼:

;;boot.s;; boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself; out of the way to address 0x90000, and jumps there.;; It then loads the system at 0x10000, using BIOS interrupts. Thereafter; it disables all interrupts, moves the system down to 0x0000, changes; to protected mode, and calls the start of system. System then must; RE-initialize the protected mode in it's own tables, and enable; interrupts as needed.;; NOTE! currently system is at most 8*65536 bytes long. This should be no; problem, even in the future. I want to keep it simple. This 512 kB; kernel size should be enough - in fact more would mean we'd have to move; not just these start-up routines, but also do something about the cache-; memory (block IO devices). The area left over in the lower 640 kB is meant; for these. No other memory is assumed to be "physical", ie all memory; over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match; their physical addresses.;; NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated; above the 1Mb mark as well as below. Otherwise it is mainly correct.;; NOTE 2! The boot disk type must be set at compile-time, by setting; the following equ. Having the boot-up procedure hunt for the right; disk type is severe brain-damage.; The loader has been made as simple as possible (had to, to get it; in 512 bytes with the code to move to protected mode), and continuos; read errors will result in a unbreakable loop. Reboot by hand. It; loads pretty fast by getting whole sectors at a time whenever possible.; 1.44Mb disks:sectors = 18; 1.2Mb disks:; sectors = 15; 720kB disks:; sectors = 9.globl begtext, begdata, begbss, endtext, enddata, endbss.textbegtext:.databegdata:.bssbegbss:.textBOOTSEG = 0x07c0;引導程式被載入到的地址INITSEG = 0x9000;引導程式將自己移動到的目標地址SYSSEG  = 0x1000;系統核心被載入到的地址ENDSEG= SYSSEG + SYSSIZE;系統代碼的結束位址,其中 SYSSIZE 在 Makefile 中定義entry start;程式入口標識start:movax,#BOOTSEG;將引導程式被載入的地址移動到 AX 中movds,ax;將資料區段的基址設定為引導程式的起始地址movax,#INITSEG;將上文提到的目標地址設定到 AX 中moves,ax;將 AX 中的值設定到 ES 中,使用基址加位移的方式移動地址movcx,#256;將 CX 計數器的值設定為 256subsi,si;清空 SIsubdi,di;清空 DIrep;重複執行 movw 256 次,實際上是將 512K 的資料(引導程式自身)搬移到INITSEG 處movw;搬移指令,資料定址方式,源:DS:SI,目標:ES:DIjmpigo,INITSEG;jmpi 段間跳轉指令,由於當前代碼已經被複製到 INITSEG ,在不同的段。64K 一個段go:movax,cs;將程式碼片段的地址移動到 AXmovds,ax;將 AX 的值移動到 DSmoves,ax;將 AX 的值移動到 ESmovss,ax;將 AX 的值移動到 SS,堆棧段的基址movsp,#0x400;棧頂指標,堆棧段的大小設定為 512K。堆棧可能是向下發展,否則會覆蓋 INITSEG 的代碼movah,#0x03;BIOS 10H 中斷的 03H 服務,讀取當前游標位置,dh:行,dl:列int0x10;執行 10H 中斷movcx,#24;將 CX 的值設定為 24,需要顯示字元的個數movbx,#0x0007;設定顯示內容; page 0, attribute 7 (normal)movbp,#msg1;將要顯示字元的起始地址載入到 BPmovax,#0x1301;13H顯示字串(ES:BP=顯示串地址)AL=顯示輸出方式(1:字串中只含顯示字元,其顯示內容在BL中,顯示後,游標位置改變),綜合起來功能是:向螢幕寫字串並移動游標int0x10;執行中斷; ok, we've written the message, now; we want to load the system (at 0x10000)movax,#SYSSEG;將 AX 的值設定為:SYSSEGmoves,ax;將 ES 的值設定為 SYSSEGcallread_it;調用 read_it,call 指令會使用堆棧寄存器callkill_motor;調用 kill_motor,關閉軟碟機; if the read went well we get current cursor position and save it for; posterity.movah,#0x03; read cursor posxorbh,bhint0x10; save it in known place, con_init fetchesmov[510],dx; it from 0x90510.將當前游標位置儲存到系統代碼最後一個段的最後兩個位元組; now we want to move to protected mode ...cli; 關閉中斷; first we move the system to it's rightful placemovax,#0x0000;AX 清零cld; DF 被置為 0,SI 與 DI 增加do_move:moves,ax; 設定目標程式碼片段索引:0addax,#0x1000   ;將程式碼片段索引加一cmpax,#0x9000   ;將目標程式碼片段索引與結束段索引進行比較jzend_move     ;如果相等則完成複製,跳轉到 end_movemovds,ax;複製時的原始碼段subdi,di;DI 清零subsi,si;SI 清零mov cx,#0x8000;複製的位元組數repmovsw;按 word(16bit) 的方式移動jdo_move;迴圈執行,直到複製完成; then we load the segment descriptorsend_move:movax,cs; 在完成載入系統代碼後,代碼還在當前段執行,由於沒有使用資料區段,資料都存放在當前程式碼片段,因此需要恢複 DS 到當前段基址movds,ax        ;將 DS 設定為正確的值lidtidt_48;載入中斷描述符表 load idt with 0,0lgdtgdt_48;載入通用描述元表 load gdt with whatever appropriate; that was painless, now we enable A20 參考 Intel 8042 晶片資料callempty_8042;調用empty_8042moval,#0xD1; 控制碼out#0x64,al           ; 輸出控制碼callempty_8042moval,#0xDF; A20 onout#0x60,alcallempty_8042; well, that went ok, I hope. Now we have to reprogram the interrupts :-(; we put them right after the intel-reserved hardware interrupts, at; int 0x20-0x2F. There they won't mess up anything. Sadly IBM really; messed this up with the original PC, and they haven't been able to; rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,; which is used for the internal hardware interrupts as well. We just; have to reprogram the 8259's, and it isn't fun.參考 Intel 8259 中斷控制器的晶片資料moval,#0x11; 初始化控制碼out#0x20,al; 輸出控制碼,send it to 8259A-1.word0x00eb,0x00eb; jmp $+2, jmp $+2 機器碼,由於當前在程式碼片段,會被執行,延遲作用out#0xA0,al; and to 8259A-2.word0x00eb,0x00ebmoval,#0x20; start of hardware int's (0x20)out#0x21,al.word0x00eb,0x00ebmoval,#0x28; start of hardware int's 2 (0x28)out#0xA1,al.word0x00eb,0x00ebmoval,#0x04; 8259-1 is masterout#0x21,al.word0x00eb,0x00ebmoval,#0x02; 8259-2 is slaveout#0xA1,al.word0x00eb,0x00ebmoval,#0x01; 8086 mode for bothout#0x21,al.word0x00eb,0x00ebout#0xA1,al.word0x00eb,0x00ebmoval,#0xFF; 設定中斷控制器的標誌位,關閉全部中斷 mask off all interrupts for nowout#0x21,al.word0x00eb,0x00ebout#0xA1,al; well, that certainly wasn't fun :-(. Hopefully it works, and we don't; need no steenking BIOS anyway (except for the initial loading :-).; The BIOS-routine wants lots of unnecessary data, and it's less; "interesting" anyway. This is how REAL programmers do it.;; Well, now's the time to actually move into protected mode. To make; things as simple as possible, we do no register set-up or anything,; we let the gnu-compiled 32-bit programs do that. We just jump to; absolute address 0x00000, in 32-bit protected mode.movax,#0x0001; protected mode (PE) bit PE 將被設定為 1,將要進入保護模式lmswax; lmsw是指裝入機器狀態字,即實際進入保護模式

            ;進入保護模式後,段寄存器就變成了選擇子,選擇子的結構:
            ;        16            2    1  0
            ;        ———————————————
            ;        |    索引      | TI    | RDL |
            ;        ———————————————
            ;其中TI=0時是從GDT中找描述符

jmpi0,8; jmp offset 0 of segment 8 (cs)選擇子=8,跳到GDT中的索引號為1的描述符,即程式碼片段,參考 GDT 的定義; This routine checks that the keyboard command queue is empty; No timeout is used - if this hangs there is something wrong with; the machine, and we probably couldn't proceed anyway.empty_8042:;8042 是鍵盤控制器.word0x00eb,0x00ebinal,#0x64;將#0x64連接埠的狀態值讀取到 AL中, 8042 status porttestal,#2;測試 AL 的第二位是否為 1, is input buffer full?jnzempty_8042;如果輸入緩衝區沒有滿,就迴圈執行,如果滿了,函數返回ret; This routine loads the system at address 0x10000, making sure; no 64kB boundaries are crossed. We try to load it as fast as; possible, loading whole tracks whenever we can.;; in:es - starting address segment (normally 0x1000); 讀取過程:先讀一個磁軌,再讀同磁軌的另一個盤面,然後重複上面過程讀取下一個磁軌; This routine has to be recompiled to fit another drive type,; just change the "sectors" variable at the start of the file; (originally 18, for a 1.44Mb drive);1.44Mb 的磁碟片結構:2面、80道/面、18扇區/道、512位元組/扇區,2880扇區,512位元組/扇區X 2880扇區 = 1440 KB ,每個磁軌 9K,每個段64K(7個磁軌+2個扇區),注意下面的溢出處理sread:.word 1; 當前磁軌已經讀取的扇區數head:.word 0; 當前磁頭號track:.word 0; 當前磁軌號read_it:mov ax,es;ES 是系統代碼的段基址(當前段讀滿後 ES 會被加一)test ax,#0x0fff;相當於 AX 高 4 位清零,低 4 位保持原來的值,0x0FFF 是 64die:jne die;按扇區複製,要求對齊xor bx,bx;BX 清零,用來標記起始地址,後面讀取資料後 BX 會變化rp_read:mov ax,es;將 ES 的值載入到 AX 中cmp ax,#ENDSEG;將 AX 的值與結束位址進行比較,判斷是否完成載入jb ok1_read;如果小於#ENDSEG,跳轉到 ok1_readret;過程結束,返回,通過跳轉到儲存在堆棧中的返回地址繼續執行ok1_read:;在沒有填滿資料到 #ENDSEG 時執行mov ax,#sectors;將扇區數載入到 AX 中,實際為 ALsub ax,sread;將 AX 的值減一,實際為 AL,在下面讀磁碟時 AL 代表扇區數mov cx,ax;將 CX 的值設定為 AX 的值,此時 AX 中存放的上次讀取的扇區數shl cx,#9;2 的 9 次方是 512,剩餘扇區數乘以 512Byte 等於已經讀取的位元組數add cx,bx;CX 為 16 位寄存器,最多可以表示 64K 資料,如果已經讀取的位元組數加上基址(BX)超過當前段需要溢出處理jnc ok2_read;若CF沒有置位則跳轉,即:沒有溢出就繼續執行,此處表示如果讀取的資料少於 64K 跳轉執行je ok2_read;利用零標誌ZF 作跳轉判斷條件,此處表示如果讀取的資料等於 64K 跳轉執行xor ax,ax;AX 清零,如果執行此處代碼,表示讀取發生了溢出即:當前磁軌的剩餘位元組數超過了當前段需要的位元組sub ax,bx;發生溢出,相當於0xFFFF-BX,等於還需要填寫的位元組數shr ax,#9;AX/512;每個扇區 512 Byte,此時 AX 中是還需要讀取的扇區數ok2_read:;在沒有填滿當前段時執行call read_track;調用 read_trackmov cx,ax;將 CX 設定為 AX,AX 中存放的是上次讀取的扇區數add ax,sread;將 AX 的值設定為 AX + sread = Total Sectorscmp ax,#sectors;判斷是否已經讀完jne ok3_read;如果沒有讀完,跳轉到 ok3_readmov ax,#1;將 AX 設定為 1,運行到此處是代表當前磁軌的扇區已經讀完sub ax,head;AX = AX - headjne ok4_read;如果不為 0,當前盤面已經讀完,跳轉到 ok4_readinc track;如果為 0,當前盤面沒有讀完,增加磁軌號,繼續讀下一個磁軌ok4_read:;在當前盤面讀完時執行mov head,ax;將 AX 的值儲存到 head 中xor ax,ax;將 AX 清零,繼續執行ok3_read:;在當前磁軌沒有讀完時執行mov sread,ax;將下一個需要讀取的磁軌號儲存到 sread 中shl cx,#9;將 CX 乘以 512,CX 代表剩餘讀取的扇區數,此時 CX 代表剩餘讀取的位元組數add bx,cx;BX = BX + CX,此時 BX 為下次存放資料的起始地址jnc rp_read;如果沒有超過 64K 繼續讀mov ax,es;如果超過 64K,將 ES(段基址)的值設定到 AX 中add ax,#0x1000;AH 加一mov es,ax;將 ES 設定為 AX,移動到下一個段xor bx,bx;清空 BXjmp rp_read;繼續讀當前磁軌read_track:
        ;BIOS 13H的02H功能:將從磁碟上把一個或更多的扇區內容讀進存貯器。因為這是一個低級功能,在一個操作中讀取的全部扇區必須在同一條磁軌上(磁頭號和磁軌號相同)
        ;入口參數: AH=02H ;功能號
        ; AL=扇區數
        ; CH、CL=磁軌號的低8位元、位7-6表示磁軌號的高2位,低6位放入所讀起始扇區號
        ; DH、DL=磁頭號、磁碟機代號
        ; ES:BX=資料緩衝區地址
        ;返回: AH=0:成功,AL=讀取的扇區數;
        ; 如果CF=1,AX中存放出錯狀態:AH=錯誤碼。
        ;注意: 寄存器DS、BX、CX、DX不變。
        ;磁頭號: 磁碟片A面=0;磁碟片B面=1。
        ;磁碟機代號:軟碟機A=0;軟碟機B=1;硬驅=80H
push axpush bxpush cxpush dx;保護現場,此時 AL 等於剩餘讀取的扇區數,注意:下面的代碼中 DX 作為臨時資料存放餓寄存器mov dx,track;將 DX 設定為 track(磁軌號),此時 DL 為磁軌號,DX 中是臨時資料mov cx,sread;將 CX 設定為 sread(扇區號),此時 CL 為扇區號inc cx;CX 加 1,加一的原因:磁碟的第一個扇區(512K)存放的是引導代碼,不是系統代碼,系統代碼從第二個扇區開始,除了第一次其它從 1 開始讀取 mov ch,dl;將 CH 設定為 DL,即磁軌號,此時 CH 為磁軌號-磁軌號與扇區號已經設定完成mov dx,head;DX 設定為磁頭號,此處為 DL,此時 DL 為磁頭號,DL 中是臨時資料mov dh,dl;將 DH 同樣設定為磁頭號,此時 DH 為磁頭號mov dl,#0;將 DL 設定為 0,軟碟機A=0,DL 為磁碟機代號and dx,#0x0100;DX中的值除了 DH 的最後一位被保留,其它清零(上面那句代碼可以去掉?),DH為磁頭號mov ah,#2;將 AH 設定為 2,功能號int 0x13;調用 BIOS 13H 中斷jc bad_rtpop dxpop cxpop bxpop ax;恢複現場retbad_rt:mov ax,#0mov dx,#0int 0x13;磁碟片複位,13H的00H號功能——磁碟片系統複位,AH=00H 功能號,DL=磁碟機代號,總之:將軟碟機A複位pop dxpop cxpop bxpop axjmp read_track;跳轉到 read_track,即:如果讀取當前扇區出現異常,繼續讀/* * This procedure turns off the floppy drive motor, so * that we enter the kernel in a known state, and * don't have to worry about it later. */kill_motor:push dxmov dx,#0x3f2mov al,#0outbpop dxretgdt:.word0,0,0,0; dummy.word0x07FF; 8Mb - limit=2047 (2048*4096=8Mb).word0x0000; base address=0.word0x9A00; code read/exec.word0x00C0; granularity=4096, 386.word0x07FF; 8Mb - limit=2047 (2048*4096=8Mb).word0x0000; base address=0.word0x9200; data read/write.word0x00C0; granularity=4096, 386idt_48:;.word0; idt limit=0.word0,0; idt base=0Lgdt_48:.word0x800; gdt limit=2048, 256 GDT entries.wordgdt,0x9; gdt base = 0X9xxxxmsg1:.byte 13,10.ascii "Loading system ...".byte 13,10,13,10.textendtext:.dataenddata:.bssendbss:
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.