本匯編代碼對應文章保戶模式下操作系統(tǒng)內(nèi)核如何加載用戶程序并運行 中的實際內(nèi)核代碼
- 對應的主引導扇區(qū)代碼:主引導扇區(qū)代碼
- 對應的用戶程序代碼:用戶程序代碼
;代碼清單
13-2;文件名:c13_core
.asm;文件說明:保護模式微型核心程序
;以下常量定義部分。內(nèi)核的大部分內(nèi)容都應當固定 core_code_seg_sel equ
0x38 ;內(nèi)核代碼段選擇子core_data_seg_sel equ
0x30 ;內(nèi)核數(shù)據(jù)段選擇子 sys_routine_seg_sel equ
0x28 ;系統(tǒng)公共例程代碼段的選擇子 video_ram_seg_sel equ
0x20 ;視頻顯示緩沖區(qū)的段選擇子core_stack_seg_sel equ
0x18 ;內(nèi)核堆棧段選擇子mem_0_4_gb_seg_sel equ
0x08 ;整個
0-4GB內(nèi)存的段的選擇子
;-------------------------------------------------------------------------------;以下是系統(tǒng)核心的頭部,用于加載核心程序 core_length dd core_end
;核心程序總長度#
00sys_routine_seg dd section
.sys_routine
.start
;系統(tǒng)公用例程段位置#
04core_data_seg dd section
.core_data
.start
;核心數(shù)據(jù)段位置#
08core_code_seg dd section
.core_code
.start
;核心代碼段位置#
0ccore_entry dd start
;核心代碼段入口點#
10dw core_code_seg_sel
;===============================================================================[bits
32]
;===============================================================================
SECTION sys_routine vstart
=0 ;系統(tǒng)公共例程代碼段
;-------------------------------------------------------------------------------;字符串顯示例程
put_string
: ;顯示
0終止的字符串并移動光標
;輸入:DS
:EBX
=串地址push ecx
.getc
:mov cl
,[ebx
]or cl
,cljz
.exitcall put_charinc ebxjmp
.getc
.exit
:pop ecxretf
;段間返回
;-------------------------------------------------------------------------------
put_char
: ;在當前光標處顯示一個字符
,并推進
;光標。僅用于段內(nèi)調(diào)用
;輸入:CL
=字符ASCII碼 pushad
;以下取當前光標位置mov dx
,0x3d4mov al
,0x0eout dx
,alinc dx
;0x3d5in al
,dx
;高字mov ah
,aldec dx
;0x3d4mov al
,0x0fout dx
,alinc dx
;0x3d5in al
,dx
;低字mov bx
,ax
;BX
=代表光標位置的
16位數(shù)cmp cl
,0x0d ;回車符?jnz
.put_0amov ax
,bxmov bl
,80div blmul blmov bx
,axjmp
.set_cursor
.put_0a
:cmp cl
,0x0a ;換行符?jnz
.put_otheradd bx
,80jmp
.roll_screen
.put_other
: ;正常顯示字符push esmov eax
,video_ram_seg_sel
;0xb8000段的選擇子mov es
,eaxshl bx
,1mov
[es
:bx
],clpop es
;以下將光標位置推進一個字符shr bx
,1inc bx
.roll_screen
:cmp bx
,2000 ;光標超出屏幕?滾屏jl
.set_cursorpush dspush esmov eax
,video_ram_seg_selmov ds
,eaxmov es
,eaxcldmov esi
,0xa0 ;小心!
32位模式下movsb
/w
/d mov edi
,0x00 ;使用的是esi
/edi
/ecx mov ecx
,1920rep movsdmov bx
,3840 ;清除屏幕最底一行mov ecx
,80 ;32位程序應該使用ECX
.cls
:mov word
[es
:bx
],0x0720add bx
,2loop
.clspop espop dsmov bx
,1920.set_cursor
:mov dx
,0x3d4mov al
,0x0eout dx
,alinc dx
;0x3d5mov al
,bhout dx
,aldec dx
;0x3d4mov al
,0x0fout dx
,alinc dx
;0x3d5mov al
,blout dx
,alpopadret
;-------------------------------------------------------------------------------
read_hard_disk_0
: ;從硬盤讀取一個邏輯扇區(qū)
;EAX
=邏輯扇區(qū)號
;DS
:EBX
=目標緩沖區(qū)地址
;返回:EBX
=EBX
+512push eax push ecxpush edxpush eaxmov dx
,0x1f2mov al
,1out dx
,al
;讀取的扇區(qū)數(shù)inc dx
;0x1f3pop eaxout dx
,al
;LBA地址
7~0inc dx
;0x1f4mov cl
,8shr eax
,clout dx
,al
;LBA地址
15~8inc dx
;0x1f5shr eax
,clout dx
,al
;LBA地址
23~16inc dx
;0x1f6shr eax
,clor al
,0xe0 ;第一硬盤 LBA地址
27~24out dx
,alinc dx
;0x1f7mov al
,0x20 ;讀命令out dx
,al
.waits
:in al
,dxand al
,0x88cmp al
,0x08jnz
.waits
;不忙,且硬盤已準備好數(shù)據(jù)傳輸 mov ecx
,256 ;總共要讀取的字數(shù)mov dx
,0x1f0.readw
:in ax
,dxmov
[ebx
],axadd ebx
,2loop
.readwpop edxpop ecxpop eaxretf
;段間返回
;-------------------------------------------------------------------------------
;匯編語言程序是極難一次成功,而且調(diào)試非常困難。這個例程可以提供幫助
put_hex_dword
: ;在當前光標處以十六進制形式顯示
;一個雙字并推進光標
;輸入:EDX
=要轉換并顯示的數(shù)字
;輸出:無pushadpush dsmov ax
,core_data_seg_sel
;切換到核心數(shù)據(jù)段 mov ds
,axmov ebx
,bin_hex
;指向核心數(shù)據(jù)段內(nèi)的轉換表mov ecx
,8.xlt
: rol edx
,4mov eax
,edxand eax
,0x0000000fxlatpush ecxmov cl
,al call put_charpop ecxloop
.xltpop dspopadretf
;-------------------------------------------------------------------------------
allocate_memory
: ;分配內(nèi)存
;輸入:ECX
=希望分配的字節(jié)數(shù)
;輸出:ECX
=起始線性地址 push dspush eaxpush ebxmov eax
,core_data_seg_selmov ds
,eaxmov eax
,[ram_alloc
]add eax
,ecx
;下一次分配時的起始地址
;這里應當有檢測可用內(nèi)存數(shù)量的指令mov ecx
,[ram_alloc
] ;返回分配的起始地址mov ebx
,eaxand ebx
,0xfffffffcadd ebx
,4 ;強制對齊 test eax
,0x00000003 ;下次分配的起始地址最好是
4字節(jié)對齊cmovnz eax
,ebx
;如果沒有對齊,則強制對齊 mov
[ram_alloc
],eax
;下次從該地址分配內(nèi)存
;cmovcc指令可以避免控制轉移 pop ebxpop eaxpop dsretf
;-------------------------------------------------------------------------------
set_up_gdt_descriptor
: ;在GDT內(nèi)安裝一個新的描述符
;輸入:EDX
:EAX
=描述符
;輸出:CX
=描述符的選擇子push eaxpush ebxpush edxpush dspush esmov ebx
,core_data_seg_sel
;切換到核心數(shù)據(jù)段mov ds
,ebxsgdt
[pgdt
] ;以便開始處理GDTmov ebx
,mem_0_4_gb_seg_selmov es
,ebxmovzx ebx
,word
[pgdt
] ;GDT界限 inc bx
;GDT總字節(jié)數(shù),也是下一個描述符偏移 add ebx
,[pgdt
+2] ;下一個描述符的線性地址 mov
[es
:ebx
],eaxmov
[es
:ebx
+4],edxadd word
[pgdt
],8 ;增加一個描述符的大小 lgdt
[pgdt
] ;對GDT的更改生效 mov ax
,[pgdt
] ;得到GDT界限值xor dx
,dxmov bx
,8div bx
;除以
8,去掉余數(shù)mov cx
,ax shl cx
,3 ;將索引號移到正確位置 pop espop dspop edxpop ebxpop eaxretf
;-------------------------------------------------------------------------------
make_seg_descriptor
: ;構造存儲器和系統(tǒng)的段描述符
;輸入:EAX
=線性基地址
; EBX
=段界限
; ECX
=屬性。各屬性位都在原始
; 位置,無關的位清零
;返回:EDX
:EAX
=描述符mov edx
,eaxshl eax
,16or ax
,bx
;描述符前
32位
(EAX
)構造完畢and edx
,0xffff0000 ;清除基地址中無關的位rol edx
,8bswap edx
;裝配基址的
31~24和
23~16 (80486+)xor bx
,bxor edx
,ebx
;裝配段界限的高
4位or edx
,ecx
;裝配屬性retf
;===============================================================================
SECTION core_data vstart
=0 ;系統(tǒng)核心的數(shù)據(jù)段
;-------------------------------------------------------------------------------pgdt dw
0 ;用于設置和修改GDT dd
0ram_alloc dd
0x00100000 ;下次分配內(nèi)存時的起始地址
;符號地址檢索表salt
:salt_1 db
'@PrintString'times
256-($
-salt_1
) db
0dd put_stringdw sys_routine_seg_selsalt_2 db
'@ReadDiskData'times
256-($
-salt_2
) db
0dd read_hard_disk_0dw sys_routine_seg_selsalt_3 db
'@PrintDwordAsHexString'times
256-($
-salt_3
) db
0dd put_hex_dworddw sys_routine_seg_selsalt_4 db
'@TerminateProgram'times
256-($
-salt_4
) db
0dd return_pointdw core_code_seg_selsalt_item_len equ $
-salt_4salt_items equ
($
-salt
)/salt_item_lenmessage_1 db
' If you seen this message,that means we 'db
'are now in protect mode,and the system 'db
'core is loaded,and the video display 'db
'routine works perfectly.',0x0d,0x0a,0message_5 db
' Loading user program...',0do_status db
'Done.',0x0d,0x0a,0message_6 db
0x0d,0x0a,0x0d,0x0a,0x0d,0x0adb
' User program terminated,control returned.',0bin_hex db
'0123456789ABCDEF';put_hex_dword子過程用的查找表 core_buf times
2048 db
0 ;內(nèi)核用的緩沖區(qū)esp_pointer dd
0 ;內(nèi)核用來臨時保存自己的棧指針 cpu_brnd0 db
0x0d,0x0a,' ',0cpu_brand times
52 db
0cpu_brnd1 db
0x0d,0x0a,0x0d,0x0a,0;===============================================================================
SECTION core_code vstart
=0
;-------------------------------------------------------------------------------
load_relocate_program
: ;加載并重定位用戶程序
;輸入:ESI
=起始邏輯扇區(qū)號
;返回:AX
=指向用戶程序頭部的選擇子 push ebxpush ecxpush edxpush esipush edipush dspush esmov eax
,core_data_seg_selmov ds
,eax
;切換DS到內(nèi)核數(shù)據(jù)段mov eax
,esi
;讀取程序頭部數(shù)據(jù) mov ebx
,core_buf call sys_routine_seg_sel
:read_hard_disk_0
;以下判斷整個程序有多大mov eax
,[core_buf
] ;程序尺寸mov ebx
,eaxand ebx
,0xfffffe00 ;使之
512字節(jié)對齊(能被
512整除的數(shù), add ebx
,512 ;低
9位都為
0 test eax
,0x000001ff ;程序的大小正好是
512的倍數(shù)嗎
? cmovnz eax
,ebx
;不是。使用湊整的結果 mov ecx
,eax
;實際需要申請的內(nèi)存數(shù)量call sys_routine_seg_sel
:allocate_memorymov ebx
,ecx
;ebx
-> 申請到的內(nèi)存首地址push ebx
;保存該首地址 xor edx
,edxmov ecx
,512div ecxmov ecx
,eax
;總扇區(qū)數(shù) mov eax
,mem_0_4_gb_seg_sel
;切換DS到
0-4GB的段mov ds
,eaxmov eax
,esi
;起始扇區(qū)號
.b1
:call sys_routine_seg_sel
:read_hard_disk_0inc eaxloop
.b1
;循環(huán)讀,直到讀完整個用戶程序
;建立程序頭部段描述符pop edi
;恢復程序裝載的首地址 mov eax
,edi
;程序頭部起始線性地址mov ebx
,[edi
+0x04] ;段長度dec ebx
;段界限 mov ecx
,0x00409200 ;字節(jié)粒度的數(shù)據(jù)段描述符call sys_routine_seg_sel
:make_seg_descriptorcall sys_routine_seg_sel
:set_up_gdt_descriptormov
[edi
+0x04],cx
;建立程序代碼段描述符mov eax
,ediadd eax
,[edi
+0x14] ;代碼起始線性地址mov ebx
,[edi
+0x18] ;段長度dec ebx
;段界限mov ecx
,0x00409800 ;字節(jié)粒度的代碼段描述符call sys_routine_seg_sel
:make_seg_descriptorcall sys_routine_seg_sel
:set_up_gdt_descriptormov
[edi
+0x14],cx
;建立程序數(shù)據(jù)段描述符mov eax
,ediadd eax
,[edi
+0x1c] ;數(shù)據(jù)段起始線性地址mov ebx
,[edi
+0x20] ;段長度dec ebx
;段界限mov ecx
,0x00409200 ;字節(jié)粒度的數(shù)據(jù)段描述符call sys_routine_seg_sel
:make_seg_descriptorcall sys_routine_seg_sel
:set_up_gdt_descriptormov
[edi
+0x1c],cx
;建立程序堆棧段描述符mov ecx
,[edi
+0x0c] ;4KB的倍率 mov ebx
,0x000fffffsub ebx
,ecx
;得到段界限mov eax
,4096 mul dword
[edi
+0x0c] mov ecx
,eax
;準備為堆棧分配內(nèi)存 call sys_routine_seg_sel
:allocate_memoryadd eax
,ecx
;得到堆棧的高端物理地址 mov ecx
,0x00c09600 ;4KB粒度的堆棧段描述符call sys_routine_seg_sel
:make_seg_descriptorcall sys_routine_seg_sel
:set_up_gdt_descriptormov
[edi
+0x08],cx
;重定位SALTmov eax
,[edi
+0x04]mov es
,eax
;es
-> 用戶程序頭部 mov eax
,core_data_seg_selmov ds
,eaxcldmov ecx
,[es
:0x24] ;用戶程序的SALT條目數(shù)mov edi
,0x28 ;用戶程序內(nèi)的SALT位于頭部內(nèi)
0x2c處
.b2
: push ecxpush edimov ecx
,salt_itemsmov esi
,salt
.b3
:push edipush esipush ecxmov ecx
,64 ;檢索表中,每條目的比較次數(shù) repe cmpsd
;每次比較
4字節(jié) jnz
.b4mov eax
,[esi
] ;若匹配,esi恰好指向其后的地址數(shù)據(jù)mov
[es
:edi
-256],eax
;將字符串改寫成偏移地址 mov ax
,[esi
+4]mov
[es
:edi
-252],ax
;以及段選擇子
.b4
:pop ecxpop esiadd esi
,salt_item_lenpop edi
;從頭比較 loop
.b3pop ediadd edi
,256pop ecxloop
.b2mov ax
,[es
:0x04]pop es
;恢復到調(diào)用此過程前的es段 pop ds
;恢復到調(diào)用此過程前的ds段pop edipop esipop edxpop ecxpop ebxret
;-------------------------------------------------------------------------------
start
:mov ecx
,core_data_seg_sel
;使ds指向核心數(shù)據(jù)段 mov ds
,ecxmov ebx
,message_1call sys_routine_seg_sel
:put_string
;顯示處理器品牌信息 mov eax
,0x80000002cpuidmov
[cpu_brand
+ 0x00],eaxmov
[cpu_brand
+ 0x04],ebxmov
[cpu_brand
+ 0x08],ecxmov
[cpu_brand
+ 0x0c],edxmov eax
,0x80000003cpuidmov
[cpu_brand
+ 0x10],eaxmov
[cpu_brand
+ 0x14],ebxmov
[cpu_brand
+ 0x18],ecxmov
[cpu_brand
+ 0x1c],edxmov eax
,0x80000004cpuidmov
[cpu_brand
+ 0x20],eaxmov
[cpu_brand
+ 0x24],ebxmov
[cpu_brand
+ 0x28],ecxmov
[cpu_brand
+ 0x2c],edxmov ebx
,cpu_brnd0call sys_routine_seg_sel
:put_stringmov ebx
,cpu_brandcall sys_routine_seg_sel
:put_stringmov ebx
,cpu_brnd1call sys_routine_seg_sel
:put_stringmov ebx
,message_5call sys_routine_seg_sel
:put_stringmov esi
,50 ;用戶程序位于邏輯
50扇區(qū) call load_relocate_programmov ebx
,do_statuscall sys_routine_seg_sel
:put_stringmov
[esp_pointer
],esp
;臨時保存堆棧指針mov ds
,axjmp far
[0x10] ;控制權交給用戶程序(入口點)
;堆棧可能切換 return_point
: ;用戶程序返回點mov eax
,core_data_seg_sel
;使ds指向核心數(shù)據(jù)段mov ds
,eaxmov eax
,core_stack_seg_sel
;切換回內(nèi)核自己的堆棧mov ss
,eax mov esp
,[esp_pointer
]mov ebx
,message_6call sys_routine_seg_sel
:put_string
;這里可以放置清除用戶程序各種描述符的指令
;也可以加載并啟動其它程序hlt
;===============================================================================
SECTION core_trail
;-------------------------------------------------------------------------------
core_end
:
總結
以上是生活随笔為你收集整理的【OS学习笔记】二十一 保护模式六:保户模式下操作系统内核如何加载用户程序并运行 对应的汇编代码之内核代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。