將自制的操作系統(tǒng)命名為BlackPoint
基本是按照《自己動手寫os》中的步驟來的,從軟盤啟動,調(diào)入Loader再調(diào)入Kernel
沒有完全復(fù)制書中的代碼,而是按照其中的思路來自己寫一遍,收獲頗豐啊。
第一步先構(gòu)造軟盤的啟動扇區(qū)boot.asm,使用NASM在XP下開發(fā):
fat12hdr是fat12格式軟盤的頭信息。
而floppy.asm是一些寫好的過程,因?yàn)閘oader中也要用到,所以干脆做成了一個文件。
下面是floppy.asm的代碼,他跟fat12hdr的依賴度很高:
下面是fat12hdr的內(nèi)容,抄自《自己動手寫os》
下面是address.inc,其中的內(nèi)容是啟動時os的內(nèi)存安排
在下面就是我用來偷懶的macro.inc
折騰了這么多,軟盤啟動扇區(qū)算是開發(fā)完了。一路下來算是會用NASM了。記得剛開始用NASM時那個難啊。其實(shí)萬事開頭難,在暴風(fēng)雨中堅持住,陽光自己來。
接下來是Loader的開發(fā),更是讓我叫苦不迭,每走一步都是荊棘遍野。通常是后面的bug調(diào)好了又發(fā)現(xiàn)了前面的bug,甚至多次重寫。好在loader功能不多,可以一個一個小模塊的開發(fā),再組合起來。
先亮出一個重要的頭文件,與保護(hù)模式有關(guān)的宏和定義都放置在x86.inc中:
有了保護(hù)模式的一些知識,就開始著手寫loader:
1.將elf格式的kernel.bin調(diào)入內(nèi)存。
2.檢查內(nèi)存情況,啟動分頁機(jī)制。
3.切換到保護(hù)模式,轉(zhuǎn)移控制到kernel
對1來說,有了boot的經(jīng)驗(yàn),這個應(yīng)該不成問題。
對2來說,我僅僅實(shí)現(xiàn)了算出該P(yáng)C上內(nèi)存上限,并無考慮內(nèi)存存在空洞。
對3來說,識別elf格式的地方曾經(jīng)出了一些問題,因?yàn)槲矣胾buntu下的ld鏈接出來的elf格式的kernel.bin比書中的代碼要多一個program。后來我仔細(xì)閱讀了elf格式的文檔之后修正了這個bug。這個bug是我進(jìn)入kernel之前的最后一只攔路虎,突破之后就可以開始用C寫一些東西了。
最后貼個圖,是我在vmware中用軟盤啟動后的情況:

基本是按照《自己動手寫os》中的步驟來的,從軟盤啟動,調(diào)入Loader再調(diào)入Kernel
沒有完全復(fù)制書中的代碼,而是按照其中的思路來自己寫一遍,收獲頗豐啊。
第一步先構(gòu)造軟盤的啟動扇區(qū)boot.asm,使用NASM在XP下開發(fā):
1 %include "address.inc"
2 %include "macro.inc"
3
4 org 0x7c00
5 TOP_OF_STACK EQU 0x7c00
6
7 jmp short LAB_boot
8 nop
9
10 %include "fat12hdr"
11
12 LAB_boot:
13 mov ax, cs
14 mov ds, ax
15 mov es, ax
16 mov ss, ax
17 mov sp, TOP_OF_STACK
18
19 RESET_FLOPPY
20
21 ;load FAT1
22 LOAD_SEC SBASE_FAT, START_FAT1, LENGTH_FAT1
23
24 ;load Root Entry Table
25 LOAD_SEC SBASE_RET, START_RET, LENGTH_RET
26
27 ;load loader
28 LOAD_FILE SBASE_loader, OFFSET_loader, loader_filename
29
30 jmp SBASE_loader:OFFSET_loader
31
32
33 ;strings
34 loader_filename DB `LOADER BIN`, 0
35
36 %include "floppy.asm"
37
38 TIMES (510 - ($ - $$)) DB 0
39 DW 0xaa55
啟動扇區(qū)包含了四個文件:address是系統(tǒng)的內(nèi)存布局安排,macro是我為了偷懶定義的一些宏。2 %include "macro.inc"
3
4 org 0x7c00
5 TOP_OF_STACK EQU 0x7c00
6
7 jmp short LAB_boot
8 nop
9
10 %include "fat12hdr"
11
12 LAB_boot:
13 mov ax, cs
14 mov ds, ax
15 mov es, ax
16 mov ss, ax
17 mov sp, TOP_OF_STACK
18
19 RESET_FLOPPY
20
21 ;load FAT1
22 LOAD_SEC SBASE_FAT, START_FAT1, LENGTH_FAT1
23
24 ;load Root Entry Table
25 LOAD_SEC SBASE_RET, START_RET, LENGTH_RET
26
27 ;load loader
28 LOAD_FILE SBASE_loader, OFFSET_loader, loader_filename
29
30 jmp SBASE_loader:OFFSET_loader
31
32
33 ;strings
34 loader_filename DB `LOADER BIN`, 0
35
36 %include "floppy.asm"
37
38 TIMES (510 - ($ - $$)) DB 0
39 DW 0xaa55
fat12hdr是fat12格式軟盤的頭信息。
而floppy.asm是一些寫好的過程,因?yàn)閘oader中也要用到,所以干脆做成了一個文件。
下面是floppy.asm的代碼,他跟fat12hdr的依賴度很高:
1 %include "address.inc"
2
3 ;---------------------------------------------------------------------
4
5 ;subroutine ReadSector
6 ;read bl Sections start from Section ax
7 ;result will be in es:di
8 ReadSector:
9
10 mov cl, 18
11 div cl
12 inc ah
13 mov cl, ah ;get Section
14 mov ch, al
15 shr ch, 1 ;get Cylinder
16 mov dh, al
17 and dh, 1 ;get Head
18 xor dl, dl ;disk0
19
20 mov bh, 0x2
21 push bp
22 push bx
23 mov bp, sp
24 mov bx, di
25 .1:
26 mov ax, word [bp]
27 int 0x13
28 test ah, ah
29 jnz .1
30 pop bx
31 pop bp
32 ret
33
34 ;---------------------------------------------------------------------
35
36 ;subroutine SearchFile
37 ;search file in root entry table (SBASE_RET:0)
38 ;input: filename should be stored in ds:si
39 ;output: ax will be the first cluster no. of the file
40 SearchFile:
41 mov bx, si ;ds:bx -> filename
42 mov ax, SBASE_RET
43 mov es, ax
44 xor di, di ;es:0 -> the first Root Entry
45 mov dx, 224
46 .search:
47 mov cx, 11
48 .compare:
49 mov al, byte [ds:bx]
50 cmp al, byte [es:di]
51 jne .next_entry
52 inc bx
53 inc di
54 loop .compare
55 jmp .ok
56 .next_entry:
57 mov bx, si ;ds:bx -> filename
58 and di, 0xffe0
59 add di, 32 ;es:di -> next Root Entry
60 dec dx
61 jnz .search
62 .fail:
63 mov ax, cs
64 mov ds, ax
65 mov si, .msg_fail
66 .print:
67 mov al, byte [ds:si]
68 test al, al
69 jz $
70 mov ah, 0x0e
71 int 0x10
72 inc si
73 jmp .print
74 .ok:
75 and di, 0xffe0
76 mov ax, word [es:di + 0x1a] ;get cluster no.
77 ret
78
79 ;strings
80 .msg_fail DB ` file is missing
\n\r`, 0
81
82 ;---------------------------------------------------------------------
83
84 ;subroutine LoadFile
85 ;load file started from cluster ax to es:di
86 LoadFile:
87 .1:
88 cmp ax, 0xff8
89 jae .end
90 push ax
91 add ax, 31
92 mov bl, 1
93 call ReadSector
94 pop ax
95 call GetNextClus
96 add di, 512
97 jmp .1
98 .end:
99 ret
100
101 ;---------------------------------------------------------------------
102
103 ;subroutine GetNextClus
104 ;get next clus_num of current clus(stored in ax)
105 ;FAT must be loaded in SBASE_FAT:0
106 ;result will be in ax
107 ;none of regs will be changed besides ax
108 GetNextClus:
109 push ds
110 push si
111 push bx
112
113 mov bx, SBASE_FAT
114 mov ds, bx
115 mov bx, ax ;let bx be the cluster no.
116 shr ax, 1
117 imul ax, 3
118 mov si, ax
119 test bx, 1 ;test cluster no. whether even or odd
120 jnz .odd
121 .even:
122 mov ax, word [ds:si]
123 and ax, 0xfff
124 jmp .end
125 .odd:
126 mov ax, word [ds:si + 1]
127 shr ax, 4
128 .end:
129 pop bx
130 pop si
131 pop ds
132 ret
2
3 ;---------------------------------------------------------------------
4
5 ;subroutine ReadSector
6 ;read bl Sections start from Section ax
7 ;result will be in es:di
8 ReadSector:
9
10 mov cl, 18
11 div cl
12 inc ah
13 mov cl, ah ;get Section
14 mov ch, al
15 shr ch, 1 ;get Cylinder
16 mov dh, al
17 and dh, 1 ;get Head
18 xor dl, dl ;disk0
19
20 mov bh, 0x2
21 push bp
22 push bx
23 mov bp, sp
24 mov bx, di
25 .1:
26 mov ax, word [bp]
27 int 0x13
28 test ah, ah
29 jnz .1
30 pop bx
31 pop bp
32 ret
33
34 ;---------------------------------------------------------------------
35
36 ;subroutine SearchFile
37 ;search file in root entry table (SBASE_RET:0)
38 ;input: filename should be stored in ds:si
39 ;output: ax will be the first cluster no. of the file
40 SearchFile:
41 mov bx, si ;ds:bx -> filename
42 mov ax, SBASE_RET
43 mov es, ax
44 xor di, di ;es:0 -> the first Root Entry
45 mov dx, 224
46 .search:
47 mov cx, 11
48 .compare:
49 mov al, byte [ds:bx]
50 cmp al, byte [es:di]
51 jne .next_entry
52 inc bx
53 inc di
54 loop .compare
55 jmp .ok
56 .next_entry:
57 mov bx, si ;ds:bx -> filename
58 and di, 0xffe0
59 add di, 32 ;es:di -> next Root Entry
60 dec dx
61 jnz .search
62 .fail:
63 mov ax, cs
64 mov ds, ax
65 mov si, .msg_fail
66 .print:
67 mov al, byte [ds:si]
68 test al, al
69 jz $
70 mov ah, 0x0e
71 int 0x10
72 inc si
73 jmp .print
74 .ok:
75 and di, 0xffe0
76 mov ax, word [es:di + 0x1a] ;get cluster no.
77 ret
78
79 ;strings
80 .msg_fail DB ` file is missing
\n\r`, 081
82 ;---------------------------------------------------------------------
83
84 ;subroutine LoadFile
85 ;load file started from cluster ax to es:di
86 LoadFile:
87 .1:
88 cmp ax, 0xff8
89 jae .end
90 push ax
91 add ax, 31
92 mov bl, 1
93 call ReadSector
94 pop ax
95 call GetNextClus
96 add di, 512
97 jmp .1
98 .end:
99 ret
100
101 ;---------------------------------------------------------------------
102
103 ;subroutine GetNextClus
104 ;get next clus_num of current clus(stored in ax)
105 ;FAT must be loaded in SBASE_FAT:0
106 ;result will be in ax
107 ;none of regs will be changed besides ax
108 GetNextClus:
109 push ds
110 push si
111 push bx
112
113 mov bx, SBASE_FAT
114 mov ds, bx
115 mov bx, ax ;let bx be the cluster no.
116 shr ax, 1
117 imul ax, 3
118 mov si, ax
119 test bx, 1 ;test cluster no. whether even or odd
120 jnz .odd
121 .even:
122 mov ax, word [ds:si]
123 and ax, 0xfff
124 jmp .end
125 .odd:
126 mov ax, word [ds:si + 1]
127 shr ax, 4
128 .end:
129 pop bx
130 pop si
131 pop ds
132 ret
下面是fat12hdr的內(nèi)容,抄自《自己動手寫os》
1 BS_OEMName DB 'bpt '
2 BPB_BytsPerSec DW 512
3 BPB_SecPerClus DB 1
4 BPB_RsvdSecCnt DW 1
5 BPB_NumFATs DB 2
6 BPB_RootEntCnt DW 224
7 BPB_TotSec16 DW 2880
8 BPB_Media DB 0xF0
9 BPB_FATSz16 DW 9
10 BPB_SecPerTrk DW 18
11 BPB_NumHeads DW 2
12 BPB_HiddSec DD 0
13 BPB_TotSec32 DD 0
14 BS_DrvNum DB 0
15 BS_Reserved1 DB 0
16 BS_BootSig DB 29h
17 BS_VolID DD 0
18 BS_VolLab DB 'BlackPoint '
19 BS_FileSysType DB 'FAT12 '
20
21 %ifndef __FAT12HDR
22 %define __FAT12HDR
23
24 START_FAT1 EQU 1 ;BPB_RsvdSecCnt
25 LENGTH_FAT1 EQU 9 ;BPB_FATSz16
26 START_RET EQU 19 ;BPB_RsvdSecCnt + BPB_NumFATs * BPB_FATSz16
27 LENGTH_RET EQU 14 ;BPB_RootEntCnt * 32 / BPB_BytsPerSec
28
29 %endif
2 BPB_BytsPerSec DW 512
3 BPB_SecPerClus DB 1
4 BPB_RsvdSecCnt DW 1
5 BPB_NumFATs DB 2
6 BPB_RootEntCnt DW 224
7 BPB_TotSec16 DW 2880
8 BPB_Media DB 0xF0
9 BPB_FATSz16 DW 9
10 BPB_SecPerTrk DW 18
11 BPB_NumHeads DW 2
12 BPB_HiddSec DD 0
13 BPB_TotSec32 DD 0
14 BS_DrvNum DB 0
15 BS_Reserved1 DB 0
16 BS_BootSig DB 29h
17 BS_VolID DD 0
18 BS_VolLab DB 'BlackPoint '
19 BS_FileSysType DB 'FAT12 '
20
21 %ifndef __FAT12HDR
22 %define __FAT12HDR
23
24 START_FAT1 EQU 1 ;BPB_RsvdSecCnt
25 LENGTH_FAT1 EQU 9 ;BPB_FATSz16
26 START_RET EQU 19 ;BPB_RsvdSecCnt + BPB_NumFATs * BPB_FATSz16
27 LENGTH_RET EQU 14 ;BPB_RootEntCnt * 32 / BPB_BytsPerSec
28
29 %endif
下面是address.inc,其中的內(nèi)容是啟動時os的內(nèi)存安排
1 %ifndef __ADDRESS_INC
2 %define __ADDRESS_INC
3
4 SBASE_FAT EQU 0x8000
5 SBASE_RET EQU 0x8500
6
7 SBASE_loader EQU 0x1000
8 OFFSET_loader EQU 0x00100
9 BASE_loader EQU 0x10000
10
11 SBASE_mem EQU 0x9000
12 BASE_mem EQU 0x90000
13 ;name ;offset length note
14 mem_size EQU 0 ;4 memory size
15 num_ards EQU 4 ;4 number of ards
16 mem_info EQU 8 ;20 x 50 memory block info
17
18 SBASE_elf EQU 0x2000
19 OFFSET_elf EQU 0x0
20 BASE_elf EQU 0x20000
21
22 BASE_kernel EQU 0x30000
23 KERNEL_ENTRY EQU 0x30400
24
25 BASE_page_dir EQU 0x100000
26 BASE_page_table EQU 0x101000
27
28 %endif
2 %define __ADDRESS_INC
3
4 SBASE_FAT EQU 0x8000
5 SBASE_RET EQU 0x8500
6
7 SBASE_loader EQU 0x1000
8 OFFSET_loader EQU 0x00100
9 BASE_loader EQU 0x10000
10
11 SBASE_mem EQU 0x9000
12 BASE_mem EQU 0x90000
13 ;name ;offset length note
14 mem_size EQU 0 ;4 memory size
15 num_ards EQU 4 ;4 number of ards
16 mem_info EQU 8 ;20 x 50 memory block info
17
18 SBASE_elf EQU 0x2000
19 OFFSET_elf EQU 0x0
20 BASE_elf EQU 0x20000
21
22 BASE_kernel EQU 0x30000
23 KERNEL_ENTRY EQU 0x30400
24
25 BASE_page_dir EQU 0x100000
26 BASE_page_table EQU 0x101000
27
28 %endif
在下面就是我用來偷懶的macro.inc
1 %ifndef __MACRO_INC
2 %define __MACRO_INC
3
4 ;---------------------------------------------------------------------
5
6 %macro RESET_FLOPPY 0
7 xor ah, ah
8 xor dl, dl
9 int 0x13
10
11 %endmacro
12
13 ;---------------------------------------------------------------------
14
15 %macro CLOSE_FLOPPY 0
16 mov dx, 0x3f2
17 xor al, al
18 out dx, al
19 %endmacro
20
21 ;---------------------------------------------------------------------
22
23 ;load %3 sectors from %2 to %1:0
24 %macro LOAD_SEC 3
25 push es
26 mov ax, %1
27 mov es, ax
28 xor di, di
29 mov ax, %2
30 mov bl, %3
31 call ReadSector
32 pop es
33 %endmacro
34
35 ;---------------------------------------------------------------------
36
37 ;load file(ds:%3) to %1:%2
38 %macro LOAD_FILE 3
39 mov si, %3
40 call SearchFile
41 push es
42 mov bx, %1
43 mov es, bx
44 mov di, %2
45 call LoadFile
46 pop es
47 %endmacro
48
49 ;---------------------------------------------------------------------
50
51 ;PRINT row(byte), col(byte), char(byte)
52 %macro PRINT 3
53 mov edi, (80 * (%1) + (%2)) * 2
54 mov ah, 0x0c
55 mov al, (%3)
56 mov [gs:edi], ax
57 %endmacro
58
59 ;---------------------------------------------------------------------
60
61 %endif
2 %define __MACRO_INC
3
4 ;---------------------------------------------------------------------
5
6 %macro RESET_FLOPPY 0
7 xor ah, ah
8 xor dl, dl
9 int 0x13
10
11 %endmacro
12
13 ;---------------------------------------------------------------------
14
15 %macro CLOSE_FLOPPY 0
16 mov dx, 0x3f2
17 xor al, al
18 out dx, al
19 %endmacro
20
21 ;---------------------------------------------------------------------
22
23 ;load %3 sectors from %2 to %1:0
24 %macro LOAD_SEC 3
25 push es
26 mov ax, %1
27 mov es, ax
28 xor di, di
29 mov ax, %2
30 mov bl, %3
31 call ReadSector
32 pop es
33 %endmacro
34
35 ;---------------------------------------------------------------------
36
37 ;load file(ds:%3) to %1:%2
38 %macro LOAD_FILE 3
39 mov si, %3
40 call SearchFile
41 push es
42 mov bx, %1
43 mov es, bx
44 mov di, %2
45 call LoadFile
46 pop es
47 %endmacro
48
49 ;---------------------------------------------------------------------
50
51 ;PRINT row(byte), col(byte), char(byte)
52 %macro PRINT 3
53 mov edi, (80 * (%1) + (%2)) * 2
54 mov ah, 0x0c
55 mov al, (%3)
56 mov [gs:edi], ax
57 %endmacro
58
59 ;---------------------------------------------------------------------
60
61 %endif
折騰了這么多,軟盤啟動扇區(qū)算是開發(fā)完了。一路下來算是會用NASM了。記得剛開始用NASM時那個難啊。其實(shí)萬事開頭難,在暴風(fēng)雨中堅持住,陽光自己來。
接下來是Loader的開發(fā),更是讓我叫苦不迭,每走一步都是荊棘遍野。通常是后面的bug調(diào)好了又發(fā)現(xiàn)了前面的bug,甚至多次重寫。好在loader功能不多,可以一個一個小模塊的開發(fā),再組合起來。
先亮出一個重要的頭文件,與保護(hù)模式有關(guān)的宏和定義都放置在x86.inc中:
1 %ifndef __X86_INC
2 %define __X86_INC
3
4 %define BIT(X) (1 << (X))
5
6 ;===============================================================================
7
8 ;despcritor des_t base(dword), limit(dword), attribute(word)
9 ;
10 ;attribute:
11 ; 11 10 9 8 7 6 5 4 3 2 1 0
12 ; G D 0 0 P <DPL> S < type > A
13 %macro des_t 3
14 DW ((%2) & 0xffff) ;limit 15~0
15 DW ((%1) & 0xffff) ;base 15~0
16 DB (((%1) >> 16) & 0xff) ;base 23~16
17 DB ((%3) & 0xff) ;P, DPL, S, type, A
18 DB ((((%2) >> 16) & 0xf) | (((%3) >> 4) & 0xf0))
19 ;G, D, 0, 0, limit 19~16
20 DB (((%1) >> 24) & 0xff) ;base 31~24
21 %endmacro
22
23 ;descriptor attribute
24 DA_G EQU BIT(11) ;granularity 4KB
25 DA_4G EQU DA_G
26 DA_D EQU BIT(10) ;default operation size 32bits
27 DA_B EQU DA_D
28 DA_P EQU BIT(7) ;segment present
29 DA_S EQU BIT(4)
30 DA_DATA EQU DA_S ;data segment
31 DA_E EQU BIT(2)
32 DA_W EQU BIT(1) ;(data segment)writable
33 DA_CODE EQU DA_S | BIT(3) ;code segment
34 DA_C EQU BIT(2) ;(code segment)readable
35 DA_R EQU BIT(1) ;(code segment)conforming
36 DA_A EQU BIT(0) ;Accessed
37
38 DA_LDT EQU 0x2 | DA_P
39 DA_TSS EQU 0x9 | DA_P
40 DA_CODE16 EQU DA_CODE | DA_P
41 DA_CODE32 EQU DA_CODE | DA_D | DA_P
42 DA_CODE32C EQU DA_CODE32 | DA_C
43 DA_CODE32R EQU DA_CODE32 | DA_R
44 DA_CODE32CR EQU DA_CODE32C | DA_R
45 DA_CODE32RC EQU DA_CODE32CR
46 DA_DATA16 EQU DA_DATA | DA_P
47 DA_DATA16W EQU DA_DATA16 | DA_W
48 DA_DATA32 EQU DA_DATA | DA_B | DA_P
49 DA_DATA32W EQU DA_DATA32 | DA_W
50 DA_STACK16 EQU DA_DATA | DA_W | DA_P
51 DA_STACK32 EQU DA_DATA | DA_W | DA_B | DA_P
52
53 DA_DPL0 EQU 0000_0000b ;descriptor privilege level
54 DA_DPL1 EQU 0010_0000b
55 DA_DPL2 EQU 0100_0000b
56 DA_DPL3 EQU 0110_0000b
57
58
59 ;FILL_DES_BASE descriptor, segment(word), offset(word)
60 ;fill descriptor's dase with segment:offset
61 ;eax will be modified
62 %macro FILL_DES_BASE 3
63 xor eax, eax
64 mov ax, %2
65 shl eax, 4
66 add eax, %3
67 mov word [%1 + 2], ax
68 shr eax, 16
69 mov byte [%1 + 4], al
70 mov byte [%1 + 7], ah
71 %endmacro
72
73 ;FILL_DES_LIMIT descriptor, limit(dword)
74 ;fill descriptor's limit
75 ;eax will be modified
76 %macro FILL_DES_LIMIT 2
77 mov eax, %2
78 mov word [%1], ax
79 shr eax, 16
80 and al, 0xf
81 or byte [(%1) + 6], al
82 %endmacro
83
84 ;===============================================================================
85
86 ;gate_t selector(word), offset(dword), param_count(byte), attribute(byte)
87 %macro gate_t 4
88 DW ((%2) & 0xffff) ;offset 15~0
89 DW ((%1) & 0xffff) ;selector
90 DB ((%3) & 0x11111b) ;param count
91 DB ((%4) & 0xff) ;P, DPL, S, type
92 DW (((%2) >> 16) & 0xffff) ;offset 31~24
93 %endmacro
94
95 ;gate attribute
96 GA_P EQU BIT(7) ;gate present
97
98 GA_CALL32 EQU 0xc | GA_P
99 GA_INT32 EQU 0xe | GA_P
100 GA_TRAP32 EQU 0xf | GA_P
101
102 GA_DPL0 EQU 0000_0000b ;gate privilege level
103 GA_DPL1 EQU 0010_0000b
104 GA_DPL2 EQU 0100_0000b
105 GA_DPL3 EQU 0110_0000b
106
107 ;===============================================================================
108
109 ;DEFINE_SELECTOR name, offset(word), attribute(byte)
110 %macro DEFINE_SELECTOR 3
111 %1 EQU (((%2) & 1111_1000b) | (%3))
112 %endmacro
113
114 ;selector attribute
115 SA_TI EQU BIT(2)
116 SA_GDT EQU 0
117 SA_LDT EQU SA_TI
118
119 SA_RPL0 EQU 0
120 SA_RPL1 EQU 1
121 SA_RPL2 EQU 2
122 SA_RPL3 EQU 3
123
124 ;===============================================================================
125
126 ;Page Directory/Table Entry
127 PG_P EQU BIT(0)
128 PG_R EQU 0
129 PG_W EQU BIT(1)
130 PG_S EQU 0
131 PG_U EQU BIT(2)
132 ;not complete
133
134 ;===============================================================================
135
136 %undef BIT
137
138 %endif
剛開始接觸保護(hù)模式時,被成堆的資料雷住了... 其實(shí)后來發(fā)現(xiàn)我接觸的保護(hù)模式并不難,因?yàn)槲抑挥昧似渲幸恍〔糠郑灰馨褏⒖嘉臋n看懂,都是死東西。主要內(nèi)容按照《自己動手寫os》第3章各節(jié)的內(nèi)容練一遍,再準(zhǔn)備好匯編黑皮書和Intel的開發(fā)者手冊,基本上可以過關(guān)。2 %define __X86_INC
3
4 %define BIT(X) (1 << (X))
5
6 ;===============================================================================
7
8 ;despcritor des_t base(dword), limit(dword), attribute(word)
9 ;
10 ;attribute:
11 ; 11 10 9 8 7 6 5 4 3 2 1 0
12 ; G D 0 0 P <DPL> S < type > A
13 %macro des_t 3
14 DW ((%2) & 0xffff) ;limit 15~0
15 DW ((%1) & 0xffff) ;base 15~0
16 DB (((%1) >> 16) & 0xff) ;base 23~16
17 DB ((%3) & 0xff) ;P, DPL, S, type, A
18 DB ((((%2) >> 16) & 0xf) | (((%3) >> 4) & 0xf0))
19 ;G, D, 0, 0, limit 19~16
20 DB (((%1) >> 24) & 0xff) ;base 31~24
21 %endmacro
22
23 ;descriptor attribute
24 DA_G EQU BIT(11) ;granularity 4KB
25 DA_4G EQU DA_G
26 DA_D EQU BIT(10) ;default operation size 32bits
27 DA_B EQU DA_D
28 DA_P EQU BIT(7) ;segment present
29 DA_S EQU BIT(4)
30 DA_DATA EQU DA_S ;data segment
31 DA_E EQU BIT(2)
32 DA_W EQU BIT(1) ;(data segment)writable
33 DA_CODE EQU DA_S | BIT(3) ;code segment
34 DA_C EQU BIT(2) ;(code segment)readable
35 DA_R EQU BIT(1) ;(code segment)conforming
36 DA_A EQU BIT(0) ;Accessed
37
38 DA_LDT EQU 0x2 | DA_P
39 DA_TSS EQU 0x9 | DA_P
40 DA_CODE16 EQU DA_CODE | DA_P
41 DA_CODE32 EQU DA_CODE | DA_D | DA_P
42 DA_CODE32C EQU DA_CODE32 | DA_C
43 DA_CODE32R EQU DA_CODE32 | DA_R
44 DA_CODE32CR EQU DA_CODE32C | DA_R
45 DA_CODE32RC EQU DA_CODE32CR
46 DA_DATA16 EQU DA_DATA | DA_P
47 DA_DATA16W EQU DA_DATA16 | DA_W
48 DA_DATA32 EQU DA_DATA | DA_B | DA_P
49 DA_DATA32W EQU DA_DATA32 | DA_W
50 DA_STACK16 EQU DA_DATA | DA_W | DA_P
51 DA_STACK32 EQU DA_DATA | DA_W | DA_B | DA_P
52
53 DA_DPL0 EQU 0000_0000b ;descriptor privilege level
54 DA_DPL1 EQU 0010_0000b
55 DA_DPL2 EQU 0100_0000b
56 DA_DPL3 EQU 0110_0000b
57
58
59 ;FILL_DES_BASE descriptor, segment(word), offset(word)
60 ;fill descriptor's dase with segment:offset
61 ;eax will be modified
62 %macro FILL_DES_BASE 3
63 xor eax, eax
64 mov ax, %2
65 shl eax, 4
66 add eax, %3
67 mov word [%1 + 2], ax
68 shr eax, 16
69 mov byte [%1 + 4], al
70 mov byte [%1 + 7], ah
71 %endmacro
72
73 ;FILL_DES_LIMIT descriptor, limit(dword)
74 ;fill descriptor's limit
75 ;eax will be modified
76 %macro FILL_DES_LIMIT 2
77 mov eax, %2
78 mov word [%1], ax
79 shr eax, 16
80 and al, 0xf
81 or byte [(%1) + 6], al
82 %endmacro
83
84 ;===============================================================================
85
86 ;gate_t selector(word), offset(dword), param_count(byte), attribute(byte)
87 %macro gate_t 4
88 DW ((%2) & 0xffff) ;offset 15~0
89 DW ((%1) & 0xffff) ;selector
90 DB ((%3) & 0x11111b) ;param count
91 DB ((%4) & 0xff) ;P, DPL, S, type
92 DW (((%2) >> 16) & 0xffff) ;offset 31~24
93 %endmacro
94
95 ;gate attribute
96 GA_P EQU BIT(7) ;gate present
97
98 GA_CALL32 EQU 0xc | GA_P
99 GA_INT32 EQU 0xe | GA_P
100 GA_TRAP32 EQU 0xf | GA_P
101
102 GA_DPL0 EQU 0000_0000b ;gate privilege level
103 GA_DPL1 EQU 0010_0000b
104 GA_DPL2 EQU 0100_0000b
105 GA_DPL3 EQU 0110_0000b
106
107 ;===============================================================================
108
109 ;DEFINE_SELECTOR name, offset(word), attribute(byte)
110 %macro DEFINE_SELECTOR 3
111 %1 EQU (((%2) & 1111_1000b) | (%3))
112 %endmacro
113
114 ;selector attribute
115 SA_TI EQU BIT(2)
116 SA_GDT EQU 0
117 SA_LDT EQU SA_TI
118
119 SA_RPL0 EQU 0
120 SA_RPL1 EQU 1
121 SA_RPL2 EQU 2
122 SA_RPL3 EQU 3
123
124 ;===============================================================================
125
126 ;Page Directory/Table Entry
127 PG_P EQU BIT(0)
128 PG_R EQU 0
129 PG_W EQU BIT(1)
130 PG_S EQU 0
131 PG_U EQU BIT(2)
132 ;not complete
133
134 ;===============================================================================
135
136 %undef BIT
137
138 %endif
有了保護(hù)模式的一些知識,就開始著手寫loader:
1 %include "address.inc"
2 %include "macro.inc"
3 %include "x86.inc"
4
5 org 0x100
6 TOP_OF_STACK EQU 0x100
7
8 ;===============================================================================
9 LAB_loader:
10 mov ax, cs
11 mov ds, ax
12 mov es, ax
13 mov ss, ax
14 mov sp, TOP_OF_STACK
15
16 RESET_FLOPPY
17 LOAD_FILE SBASE_elf, OFFSET_elf, kernel_filename
18 CLOSE_FLOPPY
19
20 call CheckMem
21 shr eax, 20
22 call PrintEAX
23 mov si, msg_RAM
24 call PrintString
25
26 lgdt [gdtr]
27
28 cli
29
30 in al, 0x92
31 or al, 0000_0010b
32 out 0x92, al
33
34 mov eax, cr0
35 or eax, 1
36 mov cr0, eax
37
38 jmp dword gsel_flat_code:(BASE_loader + section.SEG_code32.start)
39
40 ;strings
41 kernel_filename DB "KERNEL BIN", 0
42 msg_RAM DB `MB Memory
\n\r`, 0
43
44 ;---------------------------------------------------------------------
45
46 ;subroutine CheckMem
47 ;none of segment regs will be modified
48 CheckMem:
49 push ds
50 push es
51
52 mov ax, SBASE_mem
53 mov ds, ax
54 mov es, ax
55 xor ebx, ebx
56 mov edi, mem_info
57 .info:
58 mov eax, 0xe820
59 mov ecx, 20
60 mov edx, 0x534D4150
61 int 0x15
62 jc .fail
63 add edi, 20
64 inc dword [num_ards]
65 test ebx, ebx
66 jnz .info
67 jmp .ok
68 .fail:
69 mov ax, cs
70 mov ds, ax
71 mov si, .msg_fail
72 .print:
73 mov al, byte [ds:si]
74 test al, al
75 jz $
76 mov ah, 0x0e
77 int 0x10
78 inc si
79 jmp .print
80 .ok:
81 mov ecx, dword [num_ards]
82 mov si, mem_info
83 .count:
84 cmp dword [si + 16], 1
85 jne .next
86 mov eax, dword [si]
87 add eax, dword [si + 8]
88 cmp eax, dword [mem_size]
89 jna .next
90 mov dword [mem_size], eax
91 .next:
92 add si, 20
93 loop .count
94
95 mov eax, dword [mem_size]
96 pop es
97 pop ds
98 ret
99
100 ;strings
101 .msg_fail DB `memeory check error
\n\r`, 0
102
103 ;---------------------------------------------------------------------
104
105 ;subroutine PrintEAX
106 ;none of regs will be modified
107 PrintEAX:
108 push eax
109 push ebx
110 push ecx
111 push edx
112
113 xor ecx, ecx
114 .cal:
115 xor edx, edx
116 mov ebx, 10
117 div ebx
118 push edx
119 inc ecx
120 test eax, eax
121 jnz .cal
122 .print:
123 pop eax
124 add al, '0'
125 call PrintAL
126 loop .print
127
128 pop edx
129 pop ecx
130 pop ebx
131 pop eax
132 ret
133
134 ;---------------------------------------------------------------------
135
136 ;subroutine PrintString
137 ;ds:si should be the address of the string end of 0
138 ;none of regs will be modified
139 PrintString:
140 push si
141 push ax
142 .1:
143 mov al, byte [ds:si]
144 test al, al
145 jz .end
146 call PrintAL
147 inc si
148 jmp .1
149 .end:
150 pop ax
151 pop si
152 ret
153
154 ;---------------------------------------------------------------------
155
156 PrintAL:
157 push ax
158 mov ah, 0xe
159 int 0x10
160 pop ax
161 ret
162
163 ;----------------------------------------------------------------------
164
165 %include "floppy.asm"
166
167 ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
168
169 ;===============================================================================
170 SECTION SEG_gdt align=8
171
172 gdes_null des_t 0, 0, 0
173 gdes_flat_code des_t 0, 0xfffff, DA_CODE32R | DA_4G | DA_DPL0
174 gdes_flat_data des_t 0, 0xfffff, DA_DATA32W | DA_4G | DA_DPL0
175 gdes_video des_t 0xb8000, 0xffff, DA_DATA16W | DA_DPL3
176
177 gdtr DW $ - $$ - 1
178 DD BASE_loader + $$
179
180 DEFINE_SELECTOR gsel_flat_code, gdes_flat_code - $$, SA_GDT | SA_RPL0
181 DEFINE_SELECTOR gsel_flat_data, gdes_flat_data - $$, SA_GDT | SA_RPL0
182 DEFINE_SELECTOR gsel_video, gdes_video - $$, SA_GDT | SA_RPL0
183 ;SECTION SEG_gdt^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
184
185 ;===============================================================================
186 SECTION SEG_code32 align=16
187 BITS 32
188
189 mov ax, gsel_flat_data
190 mov ds, ax
191 mov es, ax
192 mov fs, ax
193 mov ss, ax
194 mov esp, TOP_stack
195
196 mov ax, gsel_video
197 mov gs, ax
198
199 PRINT 10, 3, 'P'
200
201 call SetupPaging
202
203 PRINT 10, 4, 'P'
204
205 call InitKernel
206
207 PRINT 10, 5, 'P'
208
209 jmp gsel_flat_code:KERNEL_ENTRY
210
211 ;---------------------------------------------------------------------
212
213 ;subroutine SetupPaging
214 SetupPaging:
215
216 mov ecx, dword [BASE_mem]
217 test ecx, 00000_00000_11111_11111_11111_11111_11b
218 jnz .another_pde
219 shr ecx, 22
220 jmp .init_pd
221 .another_pde:
222 shr ecx, 22
223 inc ecx
224 .init_pd:
225 push ecx
226 mov edi, BASE_page_dir
227 mov eax, BASE_page_table | PG_U | PG_W | PG_P
228 .1:
229 mov dword [edi], eax
230 add edi, 4
231 add eax, 4 * 1024
232 loop .1
233 .init_pt:
234 mov edi, BASE_page_table
235 mov eax, 0 | PG_U | PG_W | PG_P
236 pop ecx
237 shl ecx, 10 ;ecx *= 1024
238 .2:
239 mov dword [edi], eax
240 add edi, 4
241 add eax, 4 * 1024
242 loop .2
243
244 mov eax, BASE_page_dir
245 mov cr3, eax
246
247 mov eax, cr0
248 or eax, 1 << 31
249 mov cr0, eax
250
251 ret
252
253 ;---------------------------------------------------------------------
254
255 ;subroutine InitKernel
256 InitKernel:
257
258 xor ebx, ebx
259 mov bx, word [BASE_elf + 0x2a] ;ebx = size of one entry in the pht
260 xor edx, edx
261 mov dx, word [BASE_elf + 0x2c] ;edx = number of entries in the pht
262 mov esi, dword [BASE_elf + 0x1c];esi = offset of the pht
263 add esi, BASE_elf ;esi = address of the pht
264 .loop_edx:
265 test edx, edx
266 jz .endloop_edx
267 cmp dword [esi], 1
268 jne .2 ;the seg can be loaded if type = 1
269 push esi ;backup esi
270 mov edi, dword [esi + 0x8] ;edi = virtual address
271 xor ecx, ecx
272 mov cx, word [esi + 0x10] ;ecx = size
273 mov esi, dword [esi + 0x4] ;esi = offset of program
274 add esi, BASE_elf ;esi = address of program
275 .loop_ecx:
276 test ecx, ecx
277 jz .endloop_ecx
278 mov al, byte [esi]
279 mov byte [edi], al
280 inc esi
281 inc edi
282 dec ecx
283 jmp .loop_ecx
284 .endloop_ecx:
285 pop esi ;restore esi
286 .2:
287 add esi, ebx ;esi += pht_entry_size
288 dec edx
289 jmp .loop_edx
290 .endloop_edx:
291
292 ret
293
294 ;---------------------------------------------------------------------
295
296 LEN_code32 EQU $ - $$
297 ;SECTION SEG_code32^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
298
299 ;===============================================================================
300 SECTION SEG_stack
301 ALIGN 4
302
303 TIMES 2048 DB 0 ;2KB
304
305 TOP_stack EQU BASE_loader + $
306 ;SECTION SEG_stack^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
loader中要做下面幾件事:2 %include "macro.inc"
3 %include "x86.inc"
4
5 org 0x100
6 TOP_OF_STACK EQU 0x100
7
8 ;===============================================================================
9 LAB_loader:
10 mov ax, cs
11 mov ds, ax
12 mov es, ax
13 mov ss, ax
14 mov sp, TOP_OF_STACK
15
16 RESET_FLOPPY
17 LOAD_FILE SBASE_elf, OFFSET_elf, kernel_filename
18 CLOSE_FLOPPY
19
20 call CheckMem
21 shr eax, 20
22 call PrintEAX
23 mov si, msg_RAM
24 call PrintString
25
26 lgdt [gdtr]
27
28 cli
29
30 in al, 0x92
31 or al, 0000_0010b
32 out 0x92, al
33
34 mov eax, cr0
35 or eax, 1
36 mov cr0, eax
37
38 jmp dword gsel_flat_code:(BASE_loader + section.SEG_code32.start)
39
40 ;strings
41 kernel_filename DB "KERNEL BIN", 0
42 msg_RAM DB `MB Memory
\n\r`, 043
44 ;---------------------------------------------------------------------
45
46 ;subroutine CheckMem
47 ;none of segment regs will be modified
48 CheckMem:
49 push ds
50 push es
51
52 mov ax, SBASE_mem
53 mov ds, ax
54 mov es, ax
55 xor ebx, ebx
56 mov edi, mem_info
57 .info:
58 mov eax, 0xe820
59 mov ecx, 20
60 mov edx, 0x534D4150
61 int 0x15
62 jc .fail
63 add edi, 20
64 inc dword [num_ards]
65 test ebx, ebx
66 jnz .info
67 jmp .ok
68 .fail:
69 mov ax, cs
70 mov ds, ax
71 mov si, .msg_fail
72 .print:
73 mov al, byte [ds:si]
74 test al, al
75 jz $
76 mov ah, 0x0e
77 int 0x10
78 inc si
79 jmp .print
80 .ok:
81 mov ecx, dword [num_ards]
82 mov si, mem_info
83 .count:
84 cmp dword [si + 16], 1
85 jne .next
86 mov eax, dword [si]
87 add eax, dword [si + 8]
88 cmp eax, dword [mem_size]
89 jna .next
90 mov dword [mem_size], eax
91 .next:
92 add si, 20
93 loop .count
94
95 mov eax, dword [mem_size]
96 pop es
97 pop ds
98 ret
99
100 ;strings
101 .msg_fail DB `memeory check error
\n\r`, 0102
103 ;---------------------------------------------------------------------
104
105 ;subroutine PrintEAX
106 ;none of regs will be modified
107 PrintEAX:
108 push eax
109 push ebx
110 push ecx
111 push edx
112
113 xor ecx, ecx
114 .cal:
115 xor edx, edx
116 mov ebx, 10
117 div ebx
118 push edx
119 inc ecx
120 test eax, eax
121 jnz .cal
122 .print:
123 pop eax
124 add al, '0'
125 call PrintAL
126 loop .print
127
128 pop edx
129 pop ecx
130 pop ebx
131 pop eax
132 ret
133
134 ;---------------------------------------------------------------------
135
136 ;subroutine PrintString
137 ;ds:si should be the address of the string end of 0
138 ;none of regs will be modified
139 PrintString:
140 push si
141 push ax
142 .1:
143 mov al, byte [ds:si]
144 test al, al
145 jz .end
146 call PrintAL
147 inc si
148 jmp .1
149 .end:
150 pop ax
151 pop si
152 ret
153
154 ;---------------------------------------------------------------------
155
156 PrintAL:
157 push ax
158 mov ah, 0xe
159 int 0x10
160 pop ax
161 ret
162
163 ;----------------------------------------------------------------------
164
165 %include "floppy.asm"
166
167 ;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
168
169 ;===============================================================================
170 SECTION SEG_gdt align=8
171
172 gdes_null des_t 0, 0, 0
173 gdes_flat_code des_t 0, 0xfffff, DA_CODE32R | DA_4G | DA_DPL0
174 gdes_flat_data des_t 0, 0xfffff, DA_DATA32W | DA_4G | DA_DPL0
175 gdes_video des_t 0xb8000, 0xffff, DA_DATA16W | DA_DPL3
176
177 gdtr DW $ - $$ - 1
178 DD BASE_loader + $$
179
180 DEFINE_SELECTOR gsel_flat_code, gdes_flat_code - $$, SA_GDT | SA_RPL0
181 DEFINE_SELECTOR gsel_flat_data, gdes_flat_data - $$, SA_GDT | SA_RPL0
182 DEFINE_SELECTOR gsel_video, gdes_video - $$, SA_GDT | SA_RPL0
183 ;SECTION SEG_gdt^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
184
185 ;===============================================================================
186 SECTION SEG_code32 align=16
187 BITS 32
188
189 mov ax, gsel_flat_data
190 mov ds, ax
191 mov es, ax
192 mov fs, ax
193 mov ss, ax
194 mov esp, TOP_stack
195
196 mov ax, gsel_video
197 mov gs, ax
198
199 PRINT 10, 3, 'P'
200
201 call SetupPaging
202
203 PRINT 10, 4, 'P'
204
205 call InitKernel
206
207 PRINT 10, 5, 'P'
208
209 jmp gsel_flat_code:KERNEL_ENTRY
210
211 ;---------------------------------------------------------------------
212
213 ;subroutine SetupPaging
214 SetupPaging:
215
216 mov ecx, dword [BASE_mem]
217 test ecx, 00000_00000_11111_11111_11111_11111_11b
218 jnz .another_pde
219 shr ecx, 22
220 jmp .init_pd
221 .another_pde:
222 shr ecx, 22
223 inc ecx
224 .init_pd:
225 push ecx
226 mov edi, BASE_page_dir
227 mov eax, BASE_page_table | PG_U | PG_W | PG_P
228 .1:
229 mov dword [edi], eax
230 add edi, 4
231 add eax, 4 * 1024
232 loop .1
233 .init_pt:
234 mov edi, BASE_page_table
235 mov eax, 0 | PG_U | PG_W | PG_P
236 pop ecx
237 shl ecx, 10 ;ecx *= 1024
238 .2:
239 mov dword [edi], eax
240 add edi, 4
241 add eax, 4 * 1024
242 loop .2
243
244 mov eax, BASE_page_dir
245 mov cr3, eax
246
247 mov eax, cr0
248 or eax, 1 << 31
249 mov cr0, eax
250
251 ret
252
253 ;---------------------------------------------------------------------
254
255 ;subroutine InitKernel
256 InitKernel:
257
258 xor ebx, ebx
259 mov bx, word [BASE_elf + 0x2a] ;ebx = size of one entry in the pht
260 xor edx, edx
261 mov dx, word [BASE_elf + 0x2c] ;edx = number of entries in the pht
262 mov esi, dword [BASE_elf + 0x1c];esi = offset of the pht
263 add esi, BASE_elf ;esi = address of the pht
264 .loop_edx:
265 test edx, edx
266 jz .endloop_edx
267 cmp dword [esi], 1
268 jne .2 ;the seg can be loaded if type = 1
269 push esi ;backup esi
270 mov edi, dword [esi + 0x8] ;edi = virtual address
271 xor ecx, ecx
272 mov cx, word [esi + 0x10] ;ecx = size
273 mov esi, dword [esi + 0x4] ;esi = offset of program
274 add esi, BASE_elf ;esi = address of program
275 .loop_ecx:
276 test ecx, ecx
277 jz .endloop_ecx
278 mov al, byte [esi]
279 mov byte [edi], al
280 inc esi
281 inc edi
282 dec ecx
283 jmp .loop_ecx
284 .endloop_ecx:
285 pop esi ;restore esi
286 .2:
287 add esi, ebx ;esi += pht_entry_size
288 dec edx
289 jmp .loop_edx
290 .endloop_edx:
291
292 ret
293
294 ;---------------------------------------------------------------------
295
296 LEN_code32 EQU $ - $$
297 ;SECTION SEG_code32^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
298
299 ;===============================================================================
300 SECTION SEG_stack
301 ALIGN 4
302
303 TIMES 2048 DB 0 ;2KB
304
305 TOP_stack EQU BASE_loader + $
306 ;SECTION SEG_stack^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1.將elf格式的kernel.bin調(diào)入內(nèi)存。
2.檢查內(nèi)存情況,啟動分頁機(jī)制。
3.切換到保護(hù)模式,轉(zhuǎn)移控制到kernel
對1來說,有了boot的經(jīng)驗(yàn),這個應(yīng)該不成問題。
對2來說,我僅僅實(shí)現(xiàn)了算出該P(yáng)C上內(nèi)存上限,并無考慮內(nèi)存存在空洞。
對3來說,識別elf格式的地方曾經(jīng)出了一些問題,因?yàn)槲矣胾buntu下的ld鏈接出來的elf格式的kernel.bin比書中的代碼要多一個program。后來我仔細(xì)閱讀了elf格式的文檔之后修正了這個bug。這個bug是我進(jìn)入kernel之前的最后一只攔路虎,突破之后就可以開始用C寫一些東西了。
最后貼個圖,是我在vmware中用軟盤啟動后的情況:

