2011年9月12日 星期一

巨大的一小步 - 從真實模式進入保護模式

參考了這兩本,自己動手寫作業系統 <于淵>、自己動手寫作業系統 <楊文博>, 終於將 gas AT & T 的版本改出來。

  1 /* chapter3/1/loader.S
  2
  3    Author: Wenbo Yang <solrex@gmail.com> <http://solrex.cn>
  4
  5    This file is part of the source code of book "Write Your Own OS with Free
  6    and Open Source Software". Homepage @ <http://share.solrex.cn/WriteOS/>.
  7
  8    This file is licensed under the GNU General Public License; either
  9    version 3 of the License, or (at your option) any later version. */
 10
 11 #include "pm.h"
 12
 13 .code16
 14 .text
 15     jmp LABEL_BEGIN     /* jump over the .data section. */
 16
 17 /* NOTE! Wenbo-20080512: Actually here we put the normal .data section into
 18    the .code section. For application SW, it is not allowed. However, we are
 19    writing an OS. That is OK. Because there is no OS to complain about
 20    that behavior. :) */
 21
 22 /* Global Descriptor Table */
 23 LABEL_GDT:          Descriptor  0,                        0, 0
 24 LABEL_DESC_NORMAL:  Descriptor  0,         0xffff, DA_DRW    # Normal descriptor is for back to real mode.
 25 LABEL_DESC_CODE32:  Descriptor  0,       (SegCode32Len - 1), (DA_C + DA_32)
 26 LABEL_DESC_CODE16:  Descriptor    0,         0xffff, DA_C      # 非一致程式碼段, 16
 27 LABEL_DESC_VIDEO:   Descriptor  0xB8000,             0xffff, DA_DRW
 28
 29 .set GdtLen, (. - LABEL_GDT)  /* GDT Length */
 30
 31 GdtPtr: .2byte  (GdtLen - 1)  /* GDT Limit */
 32         .4byte  0             /* GDT Base */
 33
 34 /* GDT Selector */
 35 .set SelectorNormal, (LABEL_DESC_NORMAL - LABEL_GDT)
 36 .set    SelectorCode32, (LABEL_DESC_CODE32 - LABEL_GDT)
 37 .set SelectorCode16, (LABEL_DESC_CODE16 - LABEL_GDT)
 38 .set    SelectorVideo,  (LABEL_DESC_VIDEO  - LABEL_GDT)
 39
 40 /* Program starts here. */
 41 LABEL_BEGIN:
 42     mov     %cs, %ax    /* Move code segment address(CS) to data segment */
 43     mov     %ax, %ds    /* register(DS), ES and SS. Because we have      */
 44     mov     %ax, %es    /* embedded .data section into .code section in  */
 45     mov     %ax, %ss    /* the start(mentioned in the NOTE above).       */
 46
 47     movw     $0x100, %sp
 48     nop
 49     movw %ax, (LABEL_GO_BACK_TO_REAL+3) # modify segment value, indexed memory mode, ref  professional aeesmbly language p 102.
 50
 51     /* Initialize 16-bits code segment descriptor. */
 52     xor     %eax, %eax
 53     mov     %cs, %ax
 54     shl     $4, %eax
 55     addl    $(LABEL_SEG_CODE16), %eax
 56     movw    %ax, (LABEL_DESC_CODE16 + 2)
 57     shr     $16, %eax
 58     movb    %al, (LABEL_DESC_CODE16 + 4)
 59     movb    %ah, (LABEL_DESC_CODE16 + 7)
 60
 61     /* Initialize 32-bits code segment descriptor. */
 62     xor     %eax, %eax
 63     mov     %cs, %ax
 64     shl     $4, %eax
 65     addl    $(LABEL_SEG_CODE32), %eax
 66     movw    %ax, (LABEL_DESC_CODE32 + 2)
 67     shr     $16, %eax
 68     movb    %al, (LABEL_DESC_CODE32 + 4)
 69     movb    %ah, (LABEL_DESC_CODE32 + 7)
 70
 71     /* Prepared for loading GDTR */
 72     xor     %eax, %eax
 73     mov     %ds, %ax
 74     shl     $4, %eax
 75     add     $(LABEL_GDT), %eax      /* eax <- gdt base*/
 76     movl    %eax, (GdtPtr + 2)
 77
 78     /* Load GDTR(Global Descriptor Table Register) */
 79     lgdtw   GdtPtr
 80
 81     /* Clear Interrupt Flags */
 82     cli
 83
 84     /* Open A20 line. */
 85     inb     $0x92, %al
 86     orb     $0b00000010, %al
 87     outb    %al, $0x92
 88
 89     /* Enable protect mode, PE bit of CR0. */
 90     movl    %cr0, %eax
 91     orl     $1, %eax
 92     movl    %eax, %cr0
 93
 94     /* Mixed-Size Jump. */
 95     ljmp $SelectorCode32, $0       /* Thanks to earthengine@gmail, I got */
 96                                     /* this mixed-size jump insn of gas.  */
 97                                     /* this calls far jump (ptr 16:32) in intel manual) */
 98
 99 LABEL_REAL_ENTRY:               # 從保護模式跳回到實模式就到了這裡
100         mov     %cx, %ax
101         mov     %ax, %ds
102         mov     %ax, %es
103         mov     %ax, %ss
104
105 #        mov     sp, [SPValueInRealMode]
106
107         in      $0x92, %al
108         and     $0b11111101, %al   #  close A20 line
109         out     %al, $0x92
110
111         sti                     # 開中斷
112
113         mov     $0x4c00, %ax
114         int     $0x21             #  回到 DOS
115 # END of .code16
116
117 LABEL_SEG_CODE32:
118 .code32
119     mov     $(SelectorVideo), %ax
120     mov     %ax, %gs                /* Video segment selector(dest) */
121
122     movl    $((80 * 10 + 0) * 2), %edi
123     movb    $0xC, %ah               /* 0000: Black Back 1100: Red Front */
124     movb    $'P', %al
125
126     mov     %ax, %gs:(%edi)
127
128     /* Stop here, infinite loop. */
129 #    jmp     .
130     ljmpl     $SelectorCode16,$0
131
132 /* Get the length of 32-bit segment code. */
133 .set    SegCode32Len, . - LABEL_SEG_CODE32
134
135 LABEL_SEG_CODE16:
136 .code16
137     #jmp     .
138         # back to real mode
139         mov     $SelectorNormal, %ax
140         mov     %ax, %ds
141         mov     %ax, %es
142         mov     %ax, %fs
143         mov     %ax, %gs
144         mov     %ax, %ss
145
146         mov     %cr0, %eax
147         and     $0b11111110, %al
148         mov     %eax, %cr0
149
150
151 LABEL_GO_BACK_TO_REAL:
152 #.2byte 0xea66
153 #.4byte 0x00000000
154 #.2byte LABEL_REAL_ENTRY
155     jmp     $0, $LABEL_REAL_ENTRY      # 段位址會在程序開始處被設置成正確的值
156
157
158 .set Code16Len, . - LABEL_SEG_CODE16

↓ 進入保護模式, 印出紅色 P, 再切回真實模式並回到 DOS。
From write_os



  1 /*
  2    ref: Orange'S:一个操作系统的实现
  3    do the 5M memory r/w
  4  */
  5 /* chapter3/1/loader.S
  6
  7    Author: Wenbo Yang <solrex@gmail.com> <http://solrex.cn>
  8
  9    This file is part of the source code of book "Write Your Own OS with Free
 10    and Open Source Software". Homepage @ <http://share.solrex.cn/WriteOS/>.
 11
 12    This file is licensed under the GNU General Public License; either
 13    version 3 of the License, or (at your option) any later version. */
 14
 15 #include "pm.h"
 16
 17 .code16
 18 .text
 19     jmp LABEL_BEGIN     /* jump over the .data section. */
 20
 21 /* NOTE! Wenbo-20080512: Actually here we put the normal .data section into
 22    the .code section. For application SW, it is not allowed. However, we are
 23    writing an OS. That is OK. Because there is no OS to complain about
 24    that behavior. :) */
 25
 26 /* Global Descriptor Table */
 27 LABEL_GDT:          Descriptor  0,                        0, 0
 28 LABEL_DESC_NORMAL:  Descriptor  0,         0xffff, DA_DRW    # Normal descriptor is for back to real mode.
 29 LABEL_DESC_CODE32:  Descriptor  0,       (SegCode32Len - 1), (DA_C + DA_32)
 30 LABEL_DESC_CODE16:  Descriptor    0,         0xffff, DA_C      # 非一致程式碼段, 16
 31 LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW    # Data
 32 LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32 # Stack, 32 位
 33 LABEL_DESC_TEST: Descriptor 0x500000, 0xffff, DA_DRW
 34 LABEL_DESC_VIDEO:   Descriptor  0xB8000, 0xffff, DA_DRW
 35
 36 .set GdtLen, (. - LABEL_GDT)  /* GDT Length */
 37
 38 GdtPtr: .2byte  (GdtLen - 1)  /* GDT Limit */
 39         .4byte  0             /* GDT Base */
 40
 41 /* GDT Selector */
 42 .set SelectorNormal, (LABEL_DESC_NORMAL - LABEL_GDT)
 43 .set    SelectorCode32, (LABEL_DESC_CODE32 - LABEL_GDT)
 44 .set SelectorCode16, (LABEL_DESC_CODE16 - LABEL_GDT)
 45 .set SelectorData, (LABEL_DESC_DATA         - LABEL_GDT)
 46 .set SelectorStack, (LABEL_DESC_STACK        - LABEL_GDT)
 47 .set SelectorTest, (LABEL_DESC_TEST         - LABEL_GDT)
 48 .set    SelectorVideo,  (LABEL_DESC_VIDEO  - LABEL_GDT)
 49
 50
 51 /* Program starts here. */
 52 LABEL_BEGIN:
 53     mov     %cs, %ax    /* Move code segment address(CS) to data segment */
 54     mov     %ax, %ds    /* register(DS), ES and SS. Because we have      */
 55     mov     %ax, %es    /* embedded .data section into .code section in  */
 56     mov     %ax, %ss    /* the start(mentioned in the NOTE above).       */
 57
 58     movw     $0x100, %sp
 59     nop
 60     movw %ax, (LABEL_GO_BACK_TO_REAL+3) # modify segment value, indexed memory mode, ref  professional aeesmbly language p 102.
 61
 62     /* Initialize 16-bits code segment descriptor. */
 63     xor     %eax, %eax
 64     mov     %cs, %ax
 65     shl     $4, %eax
 66     addl    $(LABEL_SEG_CODE16), %eax
 67     movw    %ax, (LABEL_DESC_CODE16 + 2)
 68     shr     $16, %eax
 69     movb    %al, (LABEL_DESC_CODE16 + 4)
 70     movb    %ah, (LABEL_DESC_CODE16 + 7)
 71
 72     /* Initialize 32-bits code segment descriptor. */
 73     xor     %eax, %eax
 74     mov     %cs, %ax
 75     shl     $4, %eax
 76     addl    $(LABEL_SEG_CODE32), %eax
 77     movw    %ax, (LABEL_DESC_CODE32 + 2)
 78     shr     $16, %eax
 79     movb    %al, (LABEL_DESC_CODE32 + 4)
 80     movb    %ah, (LABEL_DESC_CODE32 + 7)
 81
 82  # initialize data segment descriptor
 83  xor %eax, %eax
 84  mov %ds, %ax
 85  shl $4, %eax
 86  addl $(LABEL_DATA), %eax
 87  movw %ax, (LABEL_DESC_DATA + 2)
 88  shr $16, %eax
 89  movb %al, (LABEL_DESC_DATA + 4)
 90  movb %ah, (LABEL_DESC_DATA + 7)
 91
 92  # initialize stack segment descriptor
 93  xor %eax, %eax
 94  mov %ds, %ax
 95  shl $4, %eax
 96  addl $(LABEL_STACK), %eax
 97  movw %ax, (LABEL_DESC_STACK + 2)
 98  shr $16, %eax
 99  movb %al, (LABEL_DESC_STACK + 4)
100  movb %ah, (LABEL_DESC_STACK + 7)
101
102     /* Prepared for loading GDTR */
103     xor     %eax, %eax
104     mov     %ds, %ax
105     shl     $4, %eax
106     add     $(LABEL_GDT), %eax      /* eax <- gdt base*/
107     movl    %eax, (GdtPtr + 2)
108
109     /* Load GDTR(Global Descriptor Table Register) */
110     lgdtw   GdtPtr
111
112     /* Clear Interrupt Flags */
113     cli
114
115     /* Open A20 line. */
116     inb     $0x92, %al
117     orb     $0b00000010, %al
118     outb    %al, $0x92
119
120     /* Enable protect mode, PE bit of CR0. */
121     movl    %cr0, %eax
122     orl     $1, %eax
123     movl    %eax, %cr0
124
125     /* Mixed-Size Jump. */
126     ljmp $SelectorCode32, $0       /* Thanks to earthengine@gmail, I got */
127                                     /* this mixed-size jump insn of gas.  */
128                                     /* this calls far jump (ptr 16:32) in intel manual) */
129
130 LABEL_REAL_ENTRY:               # 從保護模式跳回到實模式就到了這裡
131         mov     %cx, %ax
132         mov     %ax, %ds
133         mov     %ax, %es
134         mov     %ax, %ss
135
136 #        mov     sp, [SPValueInRealMode]
137
138         in      $0x92, %al
139         and     $0b11111101, %al   #  close A20 line
140         out     %al, $0x92
141
142         sti                     # 開中斷
143
144         mov     $0x4c00, %ax
145         int     $0x21             #  回到 DOS
146 # END of .code16
147
148 LABEL_SEG_CODE32:
149 .code32
150
151  mov $(SelectorData), %ax
152  mov %ax, %ds   # 資料段選擇子
153  mov $(SelectorTest), %ax
154  mov %ax, %es   # 測試段選擇子
155
156
157
158
159     mov     $(SelectorVideo), %ax
160     mov     %ax, %gs                /* Video segment selector(dest) */
161
162  mov $(SelectorStack), %ax
163  mov %ax, %ss   # 堆疊段選擇子
164
165  mov $(TopOfStack), %esp
166
167 /*
168     movl    $((80 * 10 + 0) * 2), %edi
169     movb    $0xC, %ah               # 0000: Black Back 1100: Red Front
170     movb    $'P', %al
171
172     mov     %ax, %gs:(%edi)
173 */
174
175  # print string "In Protect Mode now. ^-^"
176  movb $0x0c, %ah # 0000: 黑底    1100: 紅字
177  xor %esi, %esi
178  xor %edi, %edi
179  mov $(OffsetPMMessage), %esi # data string offset
180  movl $((80 * 10 + 0) * 2), %edi # 目的資料偏移。螢幕第 10 行, 第 0 列。
181  cld # Clear Direction Flag, ref: http://www.fermi.mn.it/linux/quarta/x86/cld.htm
182             # After CLD is executed, string operations will increment the index
183             # (SI and/or DI) that they use.
184 .1:
185  lodsb # For legacy mode, Load byte at address DS:(E)SI into AL.
186               # For 64-bit mode load byte at address (R)SI into AL.
187               # ref: http://siyobik.info/main/reference/instruction/LODS%2FLODSB%2FLODSW%2FLODSD%2FLODSQ
188  test %al, %al # result is 0, zf sets to 1.
189  jz .2 # zf = 1 jump
190 # mov [gs:edi], ax
191         mov     %ax, %gs:(%edi)
192  add $2, %edi
193  jmp .1
194 .2: # 顯示完畢
195
196 #push  %eax                       # Multiboot magic number
197 #   push  %ebx                       # Multiboot data structure
198
199 # call kmain
200  call DispReturn
201         #movb $0xa9, %al
202  #call DispAL
203
204  call TestRead
205  call TestWrite
206  call TestRead
207
208
209     ljmpl     $SelectorCode16,$0
210     # jmpl     $SelectorCode16,$0 # it works
211
212 # ------------------------------------------------------------------------
213 TestRead:
214  xor %esi, %esi
215  mov $8, %ecx
216 .loop:
217  mov %es:(%esi), %al
218  call DispAL
219  inc %esi
220  loop .loop
221  call DispReturn
222
223  ret
224 # TestRead 結束-----------------------------------------------------------
225
226
227 # ------------------------------------------------------------------------
228 TestWrite:
229  pushl %esi
230  pushl %edi
231  xor %esi, %esi
232  xor %edi, %edi
233  mov $(OffsetStrTest), %esi # data offset
234  cld # Clear Direction Flag, ref: http://www.fermi.mn.it/linux/quarta/x86/cld.htm
235             # After CLD is executed, string operations will increment the index
236             # (SI and/or DI) that they use.
237 .6:
238  lodsb # For legacy mode, Load byte at address DS:(E)SI into AL.
239               # For 64-bit mode load byte at address (R)SI into AL.
240               # ref: http://siyobik.info/main/reference/instruction/LODS%2FLODSB%2FLODSW%2FLODSD%2FLODSQ
241
242  test %al, %al
243  jz .5 # zf = 1 jump
244 # mov [es:edi], al
245  mov %al, %es:(%edi)
246  inc %edi
247  jmp .6
248 .5:
249
250  popl %edi
251  popl %esi
252  ret
253 # TestWrite 結束----------------------------------------------------------
254
255
256 # ------------------------------------------------------------------------
257 # 顯示 AL 中的數字
258 # 默認地:
259 # 數字已經存在 AL 中
260 # edi 始終指向要顯示的下一個字元的位置
261 # 被改變的暫存器:
262 # ax, edi
263 # ------------------------------------------------------------------------
264 DispAL:
265  pushl %ecx
266  pushl %edx
267
268  movb $0x0c, %ah # 0000: 黑底    1100: 紅字
269  movb %al, %dl
270  shr $4, %al
271  movl $2, %ecx
272 .begin:
273  andb $0x0f, %al
274  cmp $9, %al
275  ja .3          # cf=0, zf=0, above 9 (>9)
276  #addb $'0', %al
277  addb $0x30, %al
278  jmp .4
279 .3:
280  sub $0x0A, %al
281  #add $'A', %al
282  add $0x41, %al
283 .4:
284  #mov [gs:edi], ax
285  mov %ax, %gs:(%edi)
286  add $2, %edi
287
288  mov %dl, %al
289  loop .begin
290  add $2, %edi
291
292  popl %edx
293  popl %ecx
294
295  ret
296 # DispAL 結束-------------------------------------------------------------
297
298
299 # ------------------------------------------------------------------------
300 DispReturn:
301  pushl %eax
302  pushl %ebx
303  mov %edi, %eax
304  movb $160, %bl
305  divb %bl          # %eax/160, 商 al, 餘數 ah.
306  and $0x0FF, %eax
307  inc %eax         # ++ %eax
308  mov $160, %bl
309  mul %bl
310  mov %eax, %edi
311  popl %ebx
312  popl %eax
313  ret
314 # DispReturn 結束---------------------------------------------------------
315
316 /*
317 kmain:
318         pushl   %ebp
319         movl    %esp, %ebp
320         popl    %ebp
321         ret
322         .size   kmain, .-kmain
323         .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
324 */
325 #        .section        .note.GNU-stack,"",@progbits
326
327
328 /* Get the length of 32-bit segment code. */
329 .set    SegCode32Len, . - LABEL_SEG_CODE32
330
331 #[SECTION .data1]         ; 資料段
332 #ALIGN   32
333 #[BITS   32]
334 LABEL_DATA:
335 SPValueInRealMode: .2byte 0x0
336 # string
337 PMMessage:              .ascii      "In Protect Mode now. ^-^\0"   # 在保護模式中顯示
338 .set    OffsetPMMessage, (PMMessage - LABEL_DATA)
339 #StrTest:                .ascii "B\0"
340 StrTest:                .ascii "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0"
341 #OffsetStrTest           equ     StrTest - $$
342 .set OffsetStrTest , (StrTest - LABEL_DATA)
343 #DataLen                 equ     $ - LABEL_DATA
344 .set DataLen, .  - LABEL_DATA
345 /* 32-bit global stack segment. */
346 LABEL_STACK:
347 .space  512, 0
348 .set    TopOfStack, (. - LABEL_STACK - 1)
349
350 # END of [SECTION .data1]
351
352
353 LABEL_SEG_CODE16:
354 .code16
355     #jmp     .
356         # back to real mode
357         mov     $SelectorNormal, %ax
358         mov     %ax, %ds
359         mov     %ax, %es
360         mov     %ax, %fs
361         mov     %ax, %gs
362         mov     %ax, %ss
363
364         mov     %cr0, %eax
365         and     $0b11111110, %al
366         mov     %eax, %cr0
367
368
369 LABEL_GO_BACK_TO_REAL:
370 #.2byte 0xea66
371 #.4byte 0x00000000
372 #.2byte LABEL_REAL_ENTRY
373     jmp     $0, $LABEL_REAL_ENTRY      # 段位址會在程序開始處被設置成正確的值
374
375
376 .set Code16Len, . - LABEL_SEG_CODE16

↓ read/write 5M 的位址。


這個程式可以在 freedos 上執行, 會進入保護模式並印出一個字元 P, 再切回真實模式並回到 freedos 提示符號。

下面是 linker script:

solrex_x86_dos.ld
 1 /* chapter3/1/solrex_x86_dos.ld
 2 
 3    Author: Wenbo Yang <solrex@gmail.com> <http://solrex.cn>
 4 
 5    This file is part of the source code of book "Write Your Own OS with Free
 6    and Open Source Software". Homepage @ <http://share.solrex.cn/WriteOS/>.
 7 
 8    This file is licensed under the GNU General Public License; either
 9    version 3 of the License, or (at your option) any later version. */
10 
11 SECTIONS
12 {
13   . = 0x0100;
14   .text :
15   {
16     _ftext = .;
17   } = 0
18 }

使用以下指令編譯並聯結:
gcc -c loader_back_to_dos.S
ld loader_back_to_dos.o -o loader_back_to_dos.elf -Tsolrex_x86_dos.ld
objcopy -R .pdr -R .comment -R.note -S -O binary loader_back_to_dos.elf loader_back_to_dos.com


測試:
http://bochs.sourceforge.net/diskimages.html 下載 freedos image, untar 之後, 使用 qemu 即可測試。

qemu -fda freedos-img/a.img -fdb pm.img

pm.img 是自己動手寫作業系統 <于淵>裡頭提供的軟碟 image, 透過

sudo mount -o loop pm.img /mnt/floppy/
sudo cp loader_back_to_dos.com  /mnt/floppy/t.com
sudo umount /mnt/floppy

將 loader_back_to_dos.com copy 到此軟碟 image (t.com)。

沒錯誤的話, 就會看到紅色的 P 字元, 並回到 dos prompt。
a: 是 a.img (freedos), b: 就會看到這軟碟 image 的程式。

印出一個 P 字元沒想到是這麼累的事情, 我希望學習 GAS AT&T 語法, 所以才會這麼辛苦, 若不堅持, 其實看自己動手寫作業系統 <于淵>就可以了。

程式碼沒解釋是吧!那怎麼看得懂, 當然看不懂, 不過這部份請自己看自己動手寫作業系統 <于淵>, 書上寫的很清楚, 但是我的組合語言功力不夠, 短短 92 行程式, 花了我半天時間才看懂, 不過保護模式的硬體架構需要先了解。不想花錢就看自己動手寫作業系統 <楊文博>這本, 接下來的路雖然還很長, 不過應該好走多了。

20110916 補充:

編成 .com 檔案果然有好處, 後來發現 dosbox 也可以順利執行, 比起這些有的沒的步驟簡單多了。
dosbox

in dosbox
mount d: /test/code
d:
a.com

/test/code 是 linux 下的目錄, mount 成 d 槽, 這樣就搞定了。
ctrl+f5 抓圖, 圖儲存在 /home_dir/.dosbox/capture。

這個程式耗費我不少腦力, 組合語言不熟看 code 就是很痛苦, 除了查 x86 組合語言指令, 還搭配組合語言書籍, 光是 DispReturn, DispAL, TestRead, TestWrite 這幾個 function call 就耗掉不少腦力, 還要轉換 nasm syntax to at&t syntax, 真是累人。

不過這些努力是有回報的, 我開始慢慢上軌道了。

dosbox 環境有些問題, 錯誤的程式碼還是可以正常執行, 目前不使用 dosbox 來做測試。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。