将自制的操作系统命名为BlackPoint
基本是按照《自己动手写os》中的步骤来的,从软盘启动,调入Loader再调入Kernel
没有完全复制书中的代码,而是按照其中的思路来自己写一遍,收获颇丰啊。
第一步先构造软盘的启动扇区boot.asm,使用NASM在XP下开发:
fat12hdr是fat12格式软盘的头信息。
而floppy.asm是一些写好的过程,因为loader中也要用到,所以干脆做成了一个文件。
下面是floppy.asm的代码,他跟fat12hdr的依赖度很高:
下面是fat12hdr的内容,抄自《自己动手写os》
下面是address.inc,其中的内容是启动时os的内存安排
在下面就是我用来偷懒的macro.inc
折腾了这么多,软盘启动扇区算是开发完了。一路下来算是会用NASM了。记得刚开始用NASM时那个难啊。其实万事开头难,在暴风雨中坚持住,阳光自己来。
接下来是Loader的开发,更是让我叫苦不迭,每走一步都是荆棘遍野。通常是后面的bug调好了又发现了前面的bug,甚至多次重写。好在loader功能不多,可以一个一个小模块的开发,再组合起来。
先亮出一个重要的头文件,与保护模式有关的宏和定义都放置在x86.inc中:
有了保护模式的一些知识,就开始着手写loader:
1.将elf格式的kernel.bin调入内存。
2.检查内存情况,启动分页机制。
3.切换到保护模式,转移控制到kernel
对1来说,有了boot的经验,这个应该不成问题。
对2来说,我仅仅实现了算出该PC上内存上限,并无考虑内存存在空洞。
对3来说,识别elf格式的地方曾经出了一些问题,因为我用ubuntu下的ld链接出来的elf格式的kernel.bin比书中的代码要多一个program。后来我仔细阅读了elf格式的文档之后修正了这个bug。这个bug是我进入kernel之前的最后一只拦路虎,突破之后就可以开始用C写一些东西了。
最后贴个图,是我在vmware中用软盘启动后的情况:
基本是按照《自己动手写os》中的步骤来的,从软盘启动,调入Loader再调入Kernel
没有完全复制书中的代码,而是按照其中的思路来自己写一遍,收获颇丰啊。
第一步先构造软盘的启动扇区boot.asm,使用NASM在XP下开发:
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
启动扇区包含了四个文件:address是系统的内存布局安排,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是一些写好的过程,因为loader中也要用到,所以干脆做成了一个文件。
下面是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`, 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
下面是fat12hdr的内容,抄自《自己动手写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,其中的内容是启动时os的内存安排
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
折腾了这么多,软盘启动扇区算是开发完了。一路下来算是会用NASM了。记得刚开始用NASM时那个难啊。其实万事开头难,在暴风雨中坚持住,阳光自己来。
接下来是Loader的开发,更是让我叫苦不迭,每走一步都是荆棘遍野。通常是后面的bug调好了又发现了前面的bug,甚至多次重写。好在loader功能不多,可以一个一个小模块的开发,再组合起来。
先亮出一个重要的头文件,与保护模式有关的宏和定义都放置在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
刚开始接触保护模式时,被成堆的资料雷住了... 其实后来发现我接触的保护模式并不难,因为我只用了其中一小部分,只要能把参考文档看懂,都是死东西。主要内容按照《自己动手写os》第3章各节的内容练一遍,再准备好汇编黑皮书和Intel的开发者手册,基本上可以过关。
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
有了保护模式的一些知识,就开始着手写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`, 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^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1.将elf格式的kernel.bin调入内存。
2.检查内存情况,启动分页机制。
3.切换到保护模式,转移控制到kernel
对1来说,有了boot的经验,这个应该不成问题。
对2来说,我仅仅实现了算出该PC上内存上限,并无考虑内存存在空洞。
对3来说,识别elf格式的地方曾经出了一些问题,因为我用ubuntu下的ld链接出来的elf格式的kernel.bin比书中的代码要多一个program。后来我仔细阅读了elf格式的文档之后修正了这个bug。这个bug是我进入kernel之前的最后一只拦路虎,突破之后就可以开始用C写一些东西了。
最后贴个图,是我在vmware中用软盘启动后的情况: