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:
使用以下指令編譯並聯結:
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 補充:
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 帳號。