博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
X86 Booting Sequence
阅读量:7067 次
发布时间:2019-06-28

本文共 18761 字,大约阅读时间需要 62 分钟。

1.BIOS

    • 0xFFFF0 電源正常啟動後,x86 CPU 會先執行 0xFFFF0,也就是 BIOS ROM 的進入點。由於 0xFFFF0 ~ 0xFFFFF 只有少的很可憐的 16 bytes,真正的 BIOS code 勢必要擺到其他位置,此時 0xFFFF0 的作用便是 jmp 到該位置執行 BIOS 程式。
    • POST (Power-On Self Test) BIOS 程式的第一個動作就是執行最基本的 POST 檢查,確保系統在開機當中可以正常運作。通常用 beep 聲來表示檢查結果。各家 BIOS 的 beep 聲都不一樣,以常見的 Award BIOS 為例:
Beep Error Message
1 short POST OK!
1 long Memory problem
1 long, 2 short Video card error
1 long, 3 short No video card or bad video RAM
Repeating beeps Memory error
High Frequency beeps Overheating CPU
For more information, visit .
    • Video Card BIOS POST 之後的首要任務就是啟動顯示卡,畢竟沒有螢幕是很痛苦的一件事情。顯示卡有自己的 BIOS,這時系統 BIOS 會掃描記憶體 0xC000:0000 ~ 0xC780:0000,也就是顯示卡 BIOS 位址,並 jmp 過去執行顯示卡的初始化。一旦成功,開機後的第一個畫面就出現了。
    • 除了顯示卡,其他有自己 BIOS 的裝置也都在這時候啟動,例如 IDE/ATA BIOS 位於 0xC8000。
    • Full POST 有了螢幕之後再來一次總檢查:
  1. Video Test: 初始化顯示卡插槽並測試顯示卡和顯示記憶體。
  2. BIOS Identification: 顯示 BIOS 版本、製造商及日期。
  3. Memory Test: 測試並顯示安裝的主記憶體總容量數。
以上是冷開機 (cold-start) 的檢查流程,若是暖開機 (warm-start) 則省略 Memory Test。這時畫面就豐富多了。
    • 除了上述檢查之外,BIOS 還會讀取存 CMOS configuration 資料。這些放在靠小電池維生的 64 bytes CMOS 當中的資料,紀錄著一些使用者可調變的系統資訊,也就是開機時按 Del 鍵進去調整的那堆東西。另外也會做一些額外的檢查,例如動態設定 IDE/ATA 裝置的參數等等,同時顯示在這個畫面當中。
    • Summary Screen 一切就緒之後,就像寫論說文「起、承、轉、合」一樣,要來「合」一下。這時 BIOS 會將整個系統資訊都顯示在螢幕上,表示開機動作大抵完成,接下來就準備交棒給下一個程式了。
    • Booting BIOS 所執行的最後一個動作就是交接,將執行權交給下一支程式,boot loader 也好,OS 也好,或者單純的一支小程式。這時會依設定的順序搜尋各開機磁碟裝置 (floppy disk, hard disk, CD-ROM, ...) 的 cylinder 0, head 0, sector 1 (對 hard disk 來說就是 Master Boot Record, MBR),並將該 512 bytes 載入至記憶體 0x0000:7C00 而 jmp 過去完成交接 (注意此時 CS:IP 為 0x0000:7C00 而不是 0x07C0:0000)。對於 MBR 而言,還要多檢查最後兩 bytes 為 0x55AA,才判定為有效的 MBR 並 jmp 過去完成交接;否則繼續搜尋下一個裝置,直到沒有裝置則顯示 "No boot device available" 之類的錯誤訊息。
Image Source:

 

    • Hello World
    • INT 0x10, INT 0x16 利用 BIOS 內建的 INT 0x10,我們可以輕易將字元印在螢幕上;INT 0x16 則可讀取鍵盤的輸入。以下為簡易的 NASM Code,在螢幕中央印出 "Hello Jasonmel!" 之後讀取字元,每讀一個字元之後將它以藍底黃字顯示在螢幕上,除了 Ctrl+C 會重新開始。

[BITS 16] ORG 0 jmp START START: ; code located at 0x0000:7C00, adjust segment registers cli mov ax, 0x07C0 mov ds, ax mov es, ax mov fs, ax mov gs, ax ; create stack mov ax, 0x0000 mov ss, ax mov sp, 0xFFFF sti ; set_VGA_mode mov ah, 0x00 ; set display mode mov al, 0x02 ; 640x200, 16 color, 80x25, 8 pages int 0x10 ; invoke BIOS ; set_cursor mov ah, 0x02 ; set cursor mov dh, 0x0C ; row number (12d) mov dl, 0x1E ; col number (31d) mov bh, 0x00 ; page number int 0x10 ; invoke BIOS ; post message mov si, msgHello call DisplayMessage ; set_cursor mov ah, 0x02 ; set cursor mov dh, 0x0D ; row number (13d) mov dl, 0x25 ; col number (37d) mov bh, 0x00 ; page number int 0x10 ; invoke BIOS GetChar: mov ah, 0x00 ; get keyboard char int 0x16 cmp al, 0x03 ; if "Ctrl+C" je END ; yes? goto end; ; print mov ah, 0x09 ; print mov bh, 0x00 ; page number mov bl, 0x1E ; property 'yellow word, blue background' mov cx, 0x01 ; times int 0x10 ; invoke BIOS jmp GetChar ; goto GetChar; END: int 0x19 ; jump to FFFF:0 (BIOS) int 0x20 ; end DisplayMessage: lodsb ; load next character or al, al ; test for NUL character jz .DONE mov ah, 0x0E ; BIOS teletype mov bh, 0x00 ; display page 0 mov bl, 0x07 ; text attribute int 0x10 ; invoke BIOS jmp DisplayMessage .DONE: ret msgHello db "Hello Jasonmel!", 0x0D, 0x0A, 0x00 TIMES 510-($-$$) DB 0 DW 0xAA55

    • 執行畫面如下,按下 Ctrl+A 會顯示下面的笑臉。
 

 

    • Boot Loader
    • Booting A Program 非常簡單!利用 BIOS INT 0x13 載入該 sector 至記憶體並且 jmp 過去即可。這時要注意避免使用一些已使用的記憶體區段,陳列如下 (部分參考自

    • ):
0x0000:0	Interrupt Vector Table	0x0040:0	BIOS Data Area	0x0050:0	PrtScr Status / Unused	0x0060:0	Image Load Address	0x07C0:0	Boot code is loaded here at startup (31k mark)	0xA000:0	EGA/VGA RAM for graphics display mode 0Dh & above	0xB000:0	MDA RAM, Hercules graphics display RAM	0xB800:0	CGA display RAM	0xC000:0	EGA/VGA BIOS ROM (thru C7FF)	0xC400:0	Video adapter ROM space	0xC600:0 256 B	PGA communication area	0xC800:0  16 KB	Hard disk adapter BIOS ROM	0xC800:5	XT Hard disk ROM format, AH=Drive, AL=Interleave	0xD000:0  32 KB	Cluster adapter BIOS ROM	0xD800:0	PCjr conventionalsoftware cartridge address	0xE000:0  64 KB	Expansion ROM space (hardwired on AT+)		 128 KB	PS/2 System ROM (thru F000)	0xF000:0	System monitor ROM			PCjr: software cartridge override address	0xF400:0	System expansion ROMs	0xF600:0	IBM ROM BASIC (AT)	0xF800:0	PCjr software cartridge override address	0xFC00:0	BIOS ROM	0xFF00:0	System ROM	0xFFA6:E	ROM graphics character table	0xFFFF:0	ROM bootstrap code	0xFFFF:5   8 B	ROM date (not applicable for all clones)	0xFFFF:E   1 B	ROM machine id
    • 其中比較關鍵的 assembly code 如下。由於讀取可能發生錯誤,因此一般會多加錯誤判斷並多讀幾次比較保險。

; Read sector (C:0, H:0, S:3) to 0x2000:0000 mov ax, 0x2000 ; es:bx = 0x2000:0000 mov es, ax ; es:bx = 0x2000:0000 mov bx, 0x0000 ; es:bx = 0x2000:0000 mov ah, 0x02 ; BIOS read sector mov al, 0x01 ; read one sector mov ch, 0x00 ; track mov cl, 0x03 ; sector mov dh, 0x00 ; head mov dl, 0x00 ; drive (0=Floppy1; 1=floppy2; 80h=HDD0; 81h=HDD1) int 0x13 ; invoke BIOS jmp 0x2000:0x0000

    • Master Boot Record (MBR) MBR 位於 hard disk 的 cylinder 0, head 0, sector 1,紀錄著 hard disk 的分割狀態,於開機時被載入至記憶體 0x0000:7C00。開始執行後,由程式依序搜尋 partition 並 check 是否為 active (80),若是,則將控制權繼續交給 active partition。為了避免覆蓋,通常 MBR 會事先將自己搬到 0x0000:0600 的位置,之後才將 partition 的 boot sector 載入至記憶體 0x0000:7C00,再 jmp 過去完成交接。

 

    • MBR 格式如下:
+--------+----------------------------------------------------+	| OFFSET | 0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F  |	+--------+----------------------------------------------------+	| 0x0000 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0010 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0020 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0030 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0040 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0050 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0060 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~	             0x0070 - 0x015F 也都是 CC 故省略	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~	| 0x0160 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0170 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0180 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x0190 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x01a0 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC CC CC |	| 0x01b0 | CC CC CC CC  CC CC CC CC  CC CC CC CC  CC CC P1 P1 |	| 0x01c0 | P1 P1 P1 P1  P1 P1 P1 P1  P1 P1 P1 P1  P1 P1 P2 P2 |	| 0x01d0 | P2 P2 P2 P2  P2 P2 P2 P2  P2 P2 P2 P2  P2 P2 P3 P3 |	| 0x01e0 | P3 P3 P3 P3  P3 P3 P3 P3  P3 P3 P3 P3  P3 P3 P4 P4 |	| 0x01f0 | P4 P4 P4 P4  P4 P4 P4 P4  P4 P4 P4 P4  P4 P4 55 AA |	+--------+----------------------------------------------------+	CC:   0x0000 to 0x01BD - Boot loader code (First 446 bytes)	P1:   0x01BE to 0x01CD - Partition entry 1	P2:   0x01CE to 0x01DD - Partition entry 2	P3:   0x01DE to 0x01ED - Partition entry 3	P4:   0x01EE to 0x01FD - Partition entry 4	55AA: 0x01FE to 0x01FF - Boot signature
    • 其中 Partition Entry 格式如下:
+------------+----+-----------+----+-----------+--------------+---------------+	|   Byte     | 0  |  1  2  3  | 4  |  5  6  7  |  8  9  A  B  |  C  D  E  F   |	+------------+----+-----------+----+-----------+--------------+---------------+	| Descrption | BI | Start HCS | FD |  End HCS  | Start Sector | Num of Sector |	+------------+----+-----------+----+-----------+--------------+---------------+	BI            (1): Boot indicator (0x00 off, 0x80 on)	Start HCS     (3): Starting head, cylinder and sector	FD            (1): Filesystem descriptor	End HCS       (3): Ending head, cylinder and sector	Start Sector  (4): Starting sector (offset to disk start)	Num of Sector (4): Number of sectors in partition
    • 找 boot sector 比較關鍵的 assembly code 如下,在此不處理延伸分割區的情形。

; Assume MBR is located at 0x0000:0800 ; Table entry should be located at 0x0000:09BE mov si, 0x09BE mov bl, 0x04 ; 4 entries EntryLoop: cmp byte [si], 0x80 ; active? jz Found ; yes add si, 0x10 ; si += 16 dec bl ; bl-- jnz EntryLoop int 0x18 Found: mov dx, [si] ; set DH/DL for INT 13 mov cx, [si+0x02] ; set CH/CL for INT 13 mov bp, si ; save table entry ; Now, ready to get next bootloader

    • Booting DOS 類似 Booting A Program,把 DOS 磁區的第一個 sector 讀入交接即可。接下來該讀入的 DOS boot sector 會將 root directory 讀入 0x0000:0500,確認前兩個 entries 是 IO.SYS 以及 MSDOS.SYS 之後,將 IO.SYS 的前 3 個 sectors 讀入 0x0000:0700,並 jmp 過去將執行權交給 IO.SYS 持續接下來的開機程序。
    • Booting FreeBSD 類似 Booting A Program,把 FreeBSD 磁區的第一個 sector 讀入交接即可,也就是三個 stage 中的 stage 1。此 sector 即為 /boot/boot0,載入執行後會將 boot sectors 也就是 slice 1 (FreeBSD 將 partition 稱為 slice) 的前 8192 bytes 讀入,進行 stage 2 booting。此時已有處理 file system 的能力,此時便可讀取檔案系統中的 kernel 進行 stage 3 完成開機。詳細情情可見下列二圖,並可參考來源網站取得進一步的細節。
Image Source:
    • Booting Linux Linux 的 booting 動作較為複雜,必須先讀懂 ext3 file system 之後將起始檔案讀入後交接。詳細動作參考下圖:
Image Source:

 

 

    • Operating System
    • Interrupt 記憶體 0x00000 到 0x00400 存放著系統的 Interrupt Vector Table,每一個 entry 共 4 bytes,紀錄著該 interrupt 的 segment (2 bytes) 及 offset (2 bytes) 以便發生 interrupt 時能跳到對應的位置去執行對應的動作。X86 的 interrupt 可列表如下:
INT (Hex)
IRQ
Common Uses
00 - 01
Exception Handlers
00: Division by Zero; 01: Single Step
02
Non-Maskable IRQ
Non-Maskable IRQ (Parity Errors)
03 - 07
Exception Handlers
03: Breakpoint; 04: Overflow; 05: Hardcopy
08
Hardware IRQ0
System Timer
09
Hardware IRQ1
Keyboard
0A
Hardware IRQ2
Redirected
0B
Hardware IRQ3
Serial Comms. COM2/COM4
0C
Hardware IRQ4
Serial Comms. COM1/COM3
0D
Hardware IRQ5
Reserved/Sound Card
0E
Hardware IRQ6
Floppy Disk Controller
0F
Hardware IRQ7
Parallel Comms.
10 - 6F
Software Interrupts
-
70
Hardware IRQ8
Real Time Clock
71
Hardware IRQ9
Redirected IRQ2
72
Hardware IRQ10
Reserved
73
Hardware IRQ11
Reserved
74
Hardware IRQ12
PS/2 Mouse
75
Hardware IRQ13
Math's Co-Processor
76
Hardware IRQ14
Hard Disk Drive
77
Hardware IRQ15
Reserved
78 - FF
Software Interrupts
-
(00 - 1F: BIOS Functions; 20 - 3F: DOS Functions; 60 - 67: User Software Interrupts; 80 - F0: BASIC Interrupts; F1 - FF: Unused)
    • 當我們要實作自己的 interrupt,只需要更改對應號碼的內容即可,以下為關鍵的 software interrupt assembly code。此時若是 hardware interrupt,則當發生了該 interrupt 時,CPU 會馬上讀取 interrupt table 並跳至對應的位置 (在下面的例子即 myINT: 位址) 的部分去執行對應的動作;若是 software interrupt,則我們可以使用 "INT 數字" 來使用該 interrupt。目前常見 OS 的系統呼叫 (system call),便是用 software interrupt 配合 registers 當參數來完成,例如 DOS 使用 INT 0x21,而 Linux 則使用 INT 0x80。必須注意的是在 interrupt 發生的同時,CPU 會將 flags、cs、ip 依序 push 進 stack 中,而在 iret 時再 pop 出來繼續執行,因此在 interrupt 當中 stack 的任何更動都必須要小心處理。

myINT: cli ; We can do something here... sti iret ; interrupt return command ; install the interrupt cli push es ; Save es segment register xor ax, ax ; Zero ax mov es, ax ; Move 0000 into es. So loading the vector table segment mov WORD [es:0x20*4], myINT ; offset of INT 20 mov WORD [es:0x20*4+2], cs ; segment of INT 20 pop es sti

    • 對於 hardware interrupt,則需要額外多做一些處理。當我們想要 enable/disable 某個 IRQ,必須更改對應 Programmable Interrupt Controller (PIC) 的 Operation Control Word (OCW) 內容。IRQ0 ~ IRQ7 的 disable bit 分別對應到 OCW1 (0x21) 之 bit0 ~ bit7,IRQ8 ~ IRQ15 則對應到 OCW2 (0xA1) 之 bit0 ~ bit7。此外,在 interrupt 結束前需送出 End Of Interrupt (EOI) 給對應的 PIC,對於 PIC1 是 outportb(0x20, 0x20);,對於 PIC2 則是 outportb(0xA0,0x20);。其中的 outportb(0xA0, 0x20) 在 asm 中為以下動作。
mov	al, 0x20	out	0xA0, al
    • 如果是以 C 來實作 hardware interrupt,則會類似以下 code。(以 IRQ1 也就是 INT 0x09 為例...)

void interrupt myISR() { asm("cli"); /* We can do something here... */ outportb(0x20, 0x20); /* Send EOI to PIC1 */ asm("sti"); } void main(void) { oldhandler = getvect(0x09); /* Save Old Interrupt Vector */ setvect(0x09, myISR); /* Set New Interrupt Vector Entry */ outportb(0x21, (inportb(0x21) & 0xFD)); /* Un-Mask (Enable) IRQ1 (0xFD = 11111101) */ /* ...Program... */ outportb(0x21, (inportb(0x21) | 0x02)); /* Mask (Disable) IRQ3 (0x02 = 00000010) */ setvect(0x09, oldhandler); /* Restore old Interrupt Vector Before Exit */ }

    • Device Driver 所謂的 device driver,說穿了就是 CPU 利用一連串的 I/O 來與 device 溝通,設定 device 的狀態、資料,或是由 device 讀取需要的資訊,可以說是軟硬體之間最底層的橋樑。I/O 方式有兩種:Direct I/O 及 Memory Mapped I/O。

 

    • 一般而言,記憶體有自己的位址空間,I/O 也有自己的位址空間,而 Direct I/O 就是利用這樣的概念,mov BYTE [0x00], al 和 out 0x00, al 是兩件截然不同的事情。然而當我們為了使用上的方便,而將部份的 I/O 動作映射到記憶體空間時,就形成所謂的 Memory Mapped I/O,此時我們對該映射之記憶體空間做存取動作,就相當於對該 device 做 I/O 的動作。聽起來有點玄,以下便以最常見最基礎的 keyboard driver 和 VGA driver 為例來說明之。
      • Keyboard Driver (INT 0x09, INT 0x16) 當我們想要從 keyboard 抓取使用者輸入時,通常會利用到 BIOS 的 INT 9 和 INT 16。其中 INT 9 為外部中斷,於使用者按下按鍵的瞬間產生中斷,將輸入的鍵值存入 BIOS 配置的 32 bytes (0x0040:001E ~ 0x0040:003D) 的 buffer 中;而 INT 16 則為內部中斷,若選項為讀取使用者之輸入,則會將 buffer 中的鍵值讀出到 al 暫存器。也就是說整個 keyboard 的運作流程可簡單示意如下圖。
        比較需要注意的是,按鍵按下 (key down) 與彈上 (key up) 分別會產生不同的鍵值,這是用來判斷如 Shift、Ctrl、Alt 等特殊按鍵是否按下的重要特性。此時就必須紀錄這些按鍵的狀態,BIOS 為此配置了 0x0040:0017 及 0x0040:0018 紀錄之。
        詳細的 INT 9 流程
        1. 送 command 0xAD 給 8042 keyboard microcontroller (port 0x64) 關閉鍵盤功能。
        2. 等待 8042 keyboard microcontroller (port 0x64) 回傳狀態 0x0A ACK。
        3. 由 on-board microcontroller (port 0x60) 回傳作對應的處理:
          • 0xEE – Echo 訊息。
          • 0xFA – Set ACK bit 訊息。
          • 0xFE – Set Resend bit 訊息。
          • 其他– 查表轉換成對應的 ASCII 碼,存入 BIOS buffer。
        4. 送 command 0xAE 給 8042 keyboard microcontroller (port 0x64) 開啟鍵盤功能。
        詳細的 INT 16 流程
        1. 等待直到 buffer 內有值。
        2. 取出 buffer 內的值。
        以下列出非常陽春只能讀取單一鍵值的 keyboard driver assembly code。若需要更詳細的功能列表,請參考 IBM PC/AT 101 Key Enhanced Keyboard 相關規格說明。
        ;; ;; Keyboard INT9 ;; INT_9: push ds pusha mov ax, 0x40 mov ds, ax ; send disable keyboard to 8042 mov al, 0xAD call Send8042 cli ; wait for data from 8042 xor cx, cx .Wait4Data: in al, 0x64 test al, 0x0A loopz .Wait4Data ; now data available, get keyboard data from 8259A in al, 0x60 ; now al contains keyboard scan code if not resend cmp al, 0x53 ja .Quit ;;;;; do translation here using your own algorithm ;;;;; ; now (ah : al) = (scan code : ascii code) push cx mov cx, ax mov ah, 0x05 int 0x16 pop cx .Quit ; send reenable the keyboard to 8042 mov al, 0xAE call Send8042 ; send EOI mov al, 0x20 out 0x20, al popa pop ds iret ; Send command in al to 8042 at port 0x64 Send8042: cli ; send the command to 8042 out 0x64, al sti ret
        ;; ;; Keyboard INT16 ;; INT_16: cmp ah, 0x00 ; Get key from buffer je .GetKey cmp ah, 0x05 ; Stor key in cx into buffer je .StoreKey iret ; Get key from buffer .GetKey sti push ds push bx mov ax, 0x40 mov ds, ax .TestKey mov bx, [0x001A] ; HeadPtr = 0x0040:0x001A cmp bx, [0x001C] ; TailPtr = 0x0040:0x001C je .TestKey ; Wait for keystroke cli ; put the key into ax mov bx, [0x001A] ; HeadPtr = 0x0040:0x001A mov ax, [bx] ; inc HeadPtr add bx, 2 cmp bx, 0x3E ; EndBuf jb .NoWrap1 mov bx, 0x1E ; Buffer .NoWrap1 mov WORD [0x001A], bx pop bx pop ds iret ; Stor key in cx into buffer .StoreKey push ds push bx mov ax, 0x40 mov ds, ax cli mov bx, [0x001C] ; TailPtr = 0x0040:0x001C push bx mov [bx], cx ; inc TailPtr add bx, 2 cmp bx, 0x3E ; EndBuf jb .NoWrap2 mov bx, 0x1E ; Buffer .NoWrap2 mov WORD [0x001C], bx cmp bx, [0x001A] ; HeadPtr = 0x0040:0x001A jne .StoreOK ; if (TailPtr != HeadPtr) goto .StoreOK pop bx ; else ignore mov [0x001C], bx sub sp, 2 .StoreOK add sp, 2 pop bx pop ds iret
      • VGA Driver (INT 0x10) 最基本的 VGA card 將畫面資料對應到記憶體位址,因此存取畫面只需存取對應位址即可。其中單色系統由 0xB000:0000 開始,彩色系統由 0xB800:0000 開始,以 word 為單位 (ASCII I.O. byte + attribute H.O. byte),皆由 (0, 0) 對應到 (79, 24),一共 4000 bytes。彩色系統提供 8 個畫面空間可供切換,分別由 0xB000:0000、0xB000:1000、0xB000:2000、...、及 0xB000:7000 開始,而單色系統僅提供一個畫面。以下為顯示一個字元之程式碼,完全沒用到 in/out,非常之歡樂。
        ; print al on location bx of screen #1 push es mov dx, 0xB800 mov es, dx shl bx, 0x01 mov BYTE [es:bx], al shr bx, 0x01 pop es
        雖然 VGA 有 memory mapped I/O 很方便,但遇到顯示狀態或游標狀態的調整,還是得用 direct I/O 來操控相關的 CRT Controller (CRTC) Registers。其中 I/O port 0x03B4和0x03B5 (單色系統)、0x03D4和0x03D5 (彩色系統) 分別對應到 CRTC Address Register 和 CRTC Data Register 的存取。舉讀取游標位置為例,先設定 Address Register 再讀取 Data Register,以下為改變目前游標位置之程式碼。
        ; read cursor location to bx push ax ; cursor location high mov al, 0x0E mov dx, 0x03D4 out dx, al mov dx, 0x03D5 in al, dx mov bh, al ; cursor location low mov al, 0x0F mov dx, 0x03D4 out dx, al mov dx, 0x03D5 in al, dx mov bl, al pop ax ;;;;; now we can do something here ;;;;; ; write cursor location from bx push ax ; cursor location high mov al, 0x0E mov dx, 0x03D4 out dx, al mov al, bh mov dx, 0x03D5 out dx, al ; cursor location low mov al, 0x0F mov dx, 0x03D4 out dx, al mov al, bl mov dx, 0x03D5 out dx, al pop ax
        關於游標位置,BIOS 於 0x0040:0050 ~ 0x0040:005F 亦配置了八組暫存 words,我們也可以善加利用,才不用每次都要大費周章的讀取。
      • Device Driver 補記 雖然表面上看起來就是一堆 in/out 好像很簡單,然而看了一下 ptt Tech_Job 板的討論,寫 driver 最大的瓶頸不在於照著規格書寫對應的功能,反而是 debug 的過程,常常會出現 bug 出在硬體或 OS 沒按照規格書實作的情形,要是硬體或 OS 廠商死不認帳,還是得自己一步步的找出癥結所在。有經驗的網友 diorite 說:「寫 Driver 的門檻只有兩個 - 不怕累、心要細,但是具備這兩個條件的人... 其實很難找。」又說:「常常都要一個人拿示波器坐在角落加班,沒有迫切金錢需要的人,通常是熬不住的。」話雖如此,就因為人才少,薪水分紅自然就比一般科技人高出許多,可以說是天下沒白吃午餐的最佳實例。
    • Context Switch 所謂的 context switch,就是做程式的切換動作。大致上是將切換前的程式執行狀態存下來,並把打算執行程式的狀態回存出來,然後執行該程式。這時候會出現一些問題 (Who, When, Where, Why and How):
      • Who - 需要存取哪些狀態? 一般而言不外乎暫存器 registers 和旗標 flags。這紀錄著程式執行到哪裡,以及一些不希望下次執行會被更動的值。
      • When - 何時存取這些狀態? 在 user applications 切換之間,必須經過 OS 的 scheduler 來做排程管理。因此整個切換流程即 app1 -> OS scheduler -> app2,其中的兩個 "->" 便是存取狀態的時機。細部動作為:把 app1 的狀態存起來,讀回 OS 的狀態,scheduler 找出下一個要執行的程式,把 OS 的狀態存起來,讀回 app2 的狀態,執行 app2。
      • Where - 要將這些狀態存在哪裡? 通常每個程式都會被 allocate 一塊專屬的 stack 空間,這就很好用啦!在程式切換的過程當中,只需要做更改 ss:sp 的動作,就可將不同的程式狀態存在它們各自的 stack 當中,既簡單又方便管理。當然,如果想存在自己另外定的記憶體區塊也沒問題,只是需要多做一些額外的管理就是了。
      • Why - 為何要存取這些狀態? 在 single task 的 OS 中,一個程式執行完才能執行下一個程式,其實是不需要做這個動作的。然而在 multi-task 的 OS 中,一個程式執行到一半可能會被 interrupt 而轉換到另一個程式,若在切換前沒事先將狀態存下來,之後回到該程式一些狀態可能就不一樣了,這時候該程式應該會很不滿而做出一些詭異或是當機的動作。所以為了不讓程式有意見,還是存一下吧。
      • How - 要如何存取這些狀態? 一言以敝之:push、pop。
        pushf	; push flags as a wordpopf	; pop flags as a wordpusha	; push ax, cx, dx, bx, sp, bp, si, dipopa	; pop di, si, bp, sp, bx, dx, cx, ax
        另外也要善加利用 call/ret 以及 int/iret 的特性。當我們執行 call addr 的時候,CPU 會將目前的 ip 暫存器 push 到 stack,再將 ip 設為 addr;而當我們執行 ret 的時候,CPU 會 pop 一個 word 到 ip 然後執行。同理 int/iret 也是一樣,只是 push/pop 的是 flags、cs、ip。
      總而言之,整個程式切換流程可以大致示意列為以下步驟:
1. app1 running...2. interrupt3. context switch from app1 to kernel4. kernel scheduler pick next application to run5. context switch form kernel to app16. interrupt return7. app2 running...
    • File System 一般而言,資料儲存設備之存取大都是以 sector 為單位,也就是 512 bytes。若任何的檔案大小都小於 512 bytes,我們大可不需要 file system,直接以 CHS 或 LBA 的方式來表示檔案即可。除非你是嵌入式系統的開發者或是史前時代的人類,不然我想一般人應該不會那麼做,一定會用到大於 512 bytes 的檔案,此時就要靠 file system 來管理檔案與儲存設備之間的對應關係。

 

    • 至於 file system 的設計則端看各位開發者的想像力。以最常見最簡單的 FAT (File Allocation Table) 為例,以數個 sectors 集結為一個 cluster 作為單位,將檔案及目錄以 table 來做一對一的對應。當檔案超過一個 cluster 大小時,則以 chain 的方式串連到另外一個 cluster,以此類推。詳細的 FAT 架構可以參考

    • ,這是目前看過最深入淺出的說明。

 

    • 基於這樣的概念,結合 FAT 及 GMail 的 label 特性,小弟亦設計了一套非常破爛的 file system,姑且稱作 JasonmelFS,若有興趣可以參考報告用的

  • Memory Management 在讀入檔案前,必須先知道該檔案要擺到記憶體哪個位置。位置的選擇,主要考量避免蓋掉其他使用中的檔案,並以發揮記憶體最大使用率,也就是盡可能的塞滿為依歸,同時演算法又不能太過遲鈍,這也正是 memory management 之所以困難的地方。為了避免蓋掉其他檔案,最簡單的做法便是使用 bitmap 的方式,以一對一的方式標記正在使用中的區塊,配置時就要避開這些位置。至於要如何充分利用記憶體以及有效率的演算法,就各憑本事了。
  • Protected Mode under construction...
  • 開機模擬動畫

 

  • Reference Links
  • (Keywords: X86, Booting, BIOS, BIOS_interrupt_call, Floppy_disk, Master_Boot_Record, Boot_sector)
  • []

转载于:https://www.cnblogs.com/sinbad-li/p/3706363.html

你可能感兴趣的文章
UITableView分页
查看>>
跟我一起数据挖掘(13)——矩阵分解
查看>>
CAShapeLayer(持续更新)
查看>>
JAVA UUID 生成唯一标识
查看>>
spring学习笔记(4)依赖注入详解
查看>>
菜鸟学自动化测试(五)-----selenium命令之定位页面元素
查看>>
【SICP练习】64 练习2.35
查看>>
PSK星座对象(constellation.cc)
查看>>
Linux链接脚本学习--lds
查看>>
Android将list数据通过LitePal保存到本地(集合保存到本地)
查看>>
hdu 1285 确定比赛名次
查看>>
Eureka微服务实战-服务提供者
查看>>
简单的原生ajax
查看>>
h5开发坑点小总结
查看>>
几分钟内提升技能的8个 JavaScript 方法!
查看>>
mac显示隐藏文件
查看>>
Android 插件化原理-好文收集(陆续中。。。)
查看>>
双亲委派模型与Tomcat类加载架构
查看>>
Highcharts tooltip显示数量和百分比
查看>>
小程序兼容iphoneX(齐刘海)代码,mpvue的写法
查看>>