ASM:《X86汇编语言-从实模式到保护模式》第13章:保护模式下内核的加载,程序的动态加载和执行...
★PART1:32位保護(hù)模式下內(nèi)核簡易模型
1. 內(nèi)核的結(jié)構(gòu),功能和加載
?????? 每個內(nèi)核的主引導(dǎo)程序都會有所不同,因?yàn)閮?nèi)核都會有不同的結(jié)構(gòu)。有時候主引導(dǎo)程序的一些段和內(nèi)核段是可以共用的(事實(shí)上加載完內(nèi)核以后就不需要主引導(dǎo)程序了),和加載一般的用戶程序一樣,主引導(dǎo)程序也是需要從硬盤中讀取程序到指定的內(nèi)存空間中。
?????? 同時,作為一個內(nèi)核,也是一個程序,而且是一個具有管理全局的能力的程序,應(yīng)該有固定的段,一般來說,內(nèi)核應(yīng)該包括以下幾個部分:
?????? 1. 公用例程段(實(shí)現(xiàn)API功能)
?????? 2. 內(nèi)核數(shù)據(jù)區(qū)(用于預(yù)讀一些數(shù)據(jù)和一些內(nèi)核內(nèi)置的保留的內(nèi)容)
?????? 3. 內(nèi)核代碼區(qū)(用于執(zhí)行內(nèi)核自己的代碼…)
?????? PS:上述段都應(yīng)該具有特權(quán)0級,特權(quán)級在教材的14章講述
?????? 在主引導(dǎo)程序中,為了加載內(nèi)核,首先應(yīng)該在GDT中加載內(nèi)核的所有的描述符。但是內(nèi)核的描述符是不確定的(因?yàn)槎谓缦藓投蔚木€性基地址不知道),所以要根據(jù)內(nèi)核頭部的信息來確定,內(nèi)核的頭部和普通的程序是一樣的,也是應(yīng)該把各個段的信息和入口點(diǎn)的相對地址列出來,同時還需要具有段的選擇子(因?yàn)閮?nèi)核是固定的,選擇子也應(yīng)該固定)。然后主引導(dǎo)程序構(gòu)建這些段的描述符(需要重定位,不過不需要再把段信息寫入內(nèi)核頭部了,因?yàn)檫x擇子有了),并且把描述符寫入GDT并且重新加載GDT即可(主要是刷新GDT的大小)。
??
?????? 那怎么根據(jù)內(nèi)核所給出的信息構(gòu)建內(nèi)核的描述符呢?教材給出了一種方法。(edi的內(nèi)容是內(nèi)核頭部的地址)
? ? ? ? ? ??
?????? 其實(shí)原理很簡單,就是給出需要加載的段的屬性(因?yàn)榧虞d什么段我們是知道的),然后只要填充好描述符的高32位和低32位就完了,教材這里用到了一個bswap(bswap r32)命令,簡單來說這個命令就是將以某個寄存器中8位為一個段的首尾交換,如圖:
?
2. 保護(hù)模式下的用戶程序的重定位和加載
?????? 首先要明白一個事情,在保護(hù)模式下(特別是在有權(quán)限管理的情況下),程序是很難進(jìn)行自我加載的,必須通過內(nèi)核來加載。當(dāng)然了,用戶程序必須符合固定的格式,才能被內(nèi)核識別和加載。一般來說,內(nèi)核需要給用戶程序提供3個段,這三個段一般是:
?????? ????????????? 1. 代碼段,
???????????????????? 2. 數(shù)據(jù)段(這里應(yīng)該有兩個區(qū)域:用戶程序頭部和文字常量區(qū)),
???????????????????? 3. 棧段,其中棧段要根據(jù)用戶程序的建議的棧段大小來定(一般不建議用戶自己創(chuàng)建棧段)。
???????????????????? (正常來講還要有堆和BSS段,但是先不搞那么復(fù)雜)
?????? 內(nèi)核從硬盤讀取了用戶程序并加載到相應(yīng)位置的時候,就讀取用戶的程序頭。我們知道,內(nèi)核有公用程序段,是專門用來給用戶程序提供公用例程的,這就是現(xiàn)代操作系統(tǒng)的API(Application Programming Interface),更直觀來說就是就是一堆庫函數(shù)名(比如C中的scanf,printf…)。早期的系統(tǒng),API是通過中斷號的方式公布的(也就是要通過軟中斷進(jìn)入),現(xiàn)在常用方法是使用符號名。正常來講,一個現(xiàn)代的操作系統(tǒng)應(yīng)該是具有一個專門的鏈接器(Linker)來構(gòu)建文件頭的。教材用一個比較原始的方式來構(gòu)建文件頭了,其實(shí)這只是一個簡單的字符串的匹配的過程而已。
(內(nèi)核數(shù)據(jù)區(qū)的符號表的樣貌)
?????? 在我們的規(guī)定中,用戶程序的符號表固定是256個字符,同時每個符號還需要預(yù)留一個區(qū)域來保存符號對應(yīng)過程的選擇子(本章過程的調(diào)用是通過GDT進(jìn)行的,下一章將會介紹調(diào)用門)。兩個字符串的比較可以使用cmpsb(字節(jié)比較),cmpsw(字比較),cmpsd(雙字比較),在16為模式下,源字符串的首地址由DS:SI指定,目的字符串的首地址由ES:DI指定,在32位模式下,則分別是DS:ESI和ES:EDI,在處理器內(nèi)部,cmps指令的操作是把兩個操作數(shù)相減,然后根據(jù)結(jié)果設(shè)置標(biāo)志寄存器中的標(biāo)志位。
?????? 單純的cmps(指令族)只比較一次,需要通過rep前綴來不斷驅(qū)動(直到ecx為0),但是這里不能單純地使用rep指令,否則就無法比較了
?
?????? 重定位符號表后,我們就要對用戶程序的段進(jìn)行重定位和內(nèi)存的分配,當(dāng)然了,理論上在讀取用戶頭部的時候就應(yīng)該開辟一個內(nèi)存放置用戶程序,但是內(nèi)核的做法是,先預(yù)讀頭部到內(nèi)核數(shù)據(jù)區(qū),然后再給用戶程序分配內(nèi)存,在現(xiàn)代操作系統(tǒng)中,內(nèi)存分配是一個很重要的工作。內(nèi)存管理程序不僅要把內(nèi)存分塊管理以便給應(yīng)用程序分配內(nèi)存,還要負(fù)責(zé)回收,還要進(jìn)行虛擬內(nèi)存管理的工作,非常復(fù)雜。這里教材用的是一個非常簡單的分配demo,沒有檢測內(nèi)存是否越界的功能,而且也沒有內(nèi)存回收。我往上加了一點(diǎn)東西。
? ? ??
?????? 接下來就是段的重定位和描述符的創(chuàng)建了,當(dāng)然這一章沒有講特權(quán)級,所以教材呢就直接把所有的程序都按特權(quán)0級的權(quán)限加載了。當(dāng)然這是一種很不好的做法,教材在14章會介紹如何用特權(quán)3級來加載程序。其實(shí)也不難,懂怎么加載內(nèi)核就知道怎么怎么加載用戶程序了,主要是棧段的分配要用到內(nèi)存分配函數(shù)而已。
??
??
?????? 這里要注意的地方是Set_New_GDT這個過程,要在GDT中安裝描述符,必須知道他的物理地址和大小,要知道這些消息,可以用sdgt指令(Store Global Descriptor Table Regiser)它用于將GDT軍訓(xùn)器的基地址和邊界信息保存到指定的內(nèi)存位置,sgdt的指令格式是sgdt m/48,這個指令不會影響任何標(biāo)志位。另外還有一個新的指令movzx
?????? movzx是一個帶零拓展的傳送(Move With Zero-Extend)指令格式為(其實(shí)就是相當(dāng)于先xor一下16/32位寄存器再寫入低位的寄存器)
movzx r16,r/m8
movzx r32,r/m8
movzx r32,r/m16
?????? 注意movzx的指令目的操作數(shù)只能是16位或者是32位的通用寄存器,源操作數(shù)只能是8位或者16位的寄存器或者內(nèi)存地址,而且目的操作數(shù)和源操作數(shù)的大小是不一樣的。和movzx指令差不多的指令是movsx指令,他是帶符號的傳送,這個指令就不像movzx一樣只會拓展0了,而是根據(jù)數(shù)的最高位來拓展。
?????? GDTR的界限部分在初始化的時候會初始化為0xFFFF,當(dāng)+1時,如果看成16位數(shù),則會是0x0000,如果看成32位數(shù),則會變成0x00010000,為了避免不必要的麻煩,直接寫成inc bx,那這樣的話開始填充描述符的時候就不是從0x00010000這個偏移地址開始了,而是0x00000000開始了。
?????? 因?yàn)閼?yīng)用程序給出的都是棧的建議大小,所以一般都是直接給出的是建議大小,所以我們直接按建議大小來乘以4KB的大小來分配內(nèi)存,要注意的是棧段是向下拓展的,高地址才是棧段的線性基地址。? ? ??
2. 用戶程序的執(zhí)行
?????? 一旦加載了用戶程序,那么我們就可以直接直接一個跳轉(zhuǎn)指令跳轉(zhuǎn)到用戶程序了
??
?????? 此時ds應(yīng)該指向用戶程序頭部。0x10剛好是偏移地址(32位)+選擇子(16位),接下來就是在用戶程序執(zhí)行一系列操作了,因?yàn)槲覀円呀?jīng)在用戶程序填入了公用例程段的選擇子,所以我們直接用fs指向用戶頭部,然后執(zhí)行這些公用例程就可以了。
?
?????? TerminateProgram是返回內(nèi)核的公用例程,返回內(nèi)核后就可以回收用戶程序用的內(nèi)存和創(chuàng)建的GDT了。
? ? ??
?????? 方法簡單粗暴,當(dāng)然學(xué)了14,15章以后我們就能學(xué)習(xí)到正確的任務(wù)切換方法了。
?????? 最后,教材還給出一種調(diào)試程序的方法,其實(shí)個人感覺沒有什么用,還不如直接在bochs看呢。應(yīng)該就是為了講xlat這個查表指令而寫的,該指令要求事先在DS:(E)BX出定義一個用于轉(zhuǎn)換編碼的表格,在16位下使用bx,在32位下使用ebx。指令執(zhí)行的時候,處理器訪問該表格,用AL寄存器作為偏移量,從表格中取出一個字節(jié),傳回AL寄存器。教材上寫了一個調(diào)試函數(shù)。
? ? ??
?? 每次將edx移動4次,總共需要移動8次,而且每次只取4位,Core_Data_Segement段的bin_hex中有16進(jìn)制的對照表。Xlat不影響任何標(biāo)志位。
★PART2:13章的代碼
1. 源代碼:
1 ;========================保護(hù)模式主引導(dǎo)扇區(qū)代碼======================== 2 core_phy_base: equ 0x00040000 ;內(nèi)核加載地址 3 core_sector_address: equ 0x00000001 ;內(nèi)核所在扇區(qū) 4 5 mov ax,cs 6 mov ss,ax 7 mov sp,0x7c00 8 9 mov eax,[cs:pgdt_base+0x7c00+0x02] 10 xor edx,edx 11 mov ebx,0x10 12 div ebx 13 14 mov ds,eax ;讓ds指向gdt位置進(jìn)行操作 15 mov ebx,edx ;別忘了還有可能出現(xiàn)偏移地址 16 17 ;---------------------描述符#0--------------------- 18 mov dword [ebx+0x00],0x00000000 ;空描述符 19 mov dword [ebx+0x04],0x00000000 20 ;---------------------描述符#1--------------------- 21 mov dword [ebx+0x08],0x0000ffff ;4GB向上拓展數(shù)據(jù)段 22 mov dword [ebx+0x0c],0x00cf9200 23 ;---------------------描述符#2--------------------- 24 mov dword [ebx+0x10],0x7c0001ff ;代碼段 25 mov dword [ebx+0x14],0x00409800 26 ;---------------------描述符#3--------------------- 27 mov dword [ebx+0x18],0x7c00fffe ;棧段 28 mov dword [ebx+0x1c],0x00cf9600 29 ;---------------------描述符#4--------------------- 30 mov dword [ebx+0x20],0x80007fff ;屏幕顯示段 31 mov dword [ebx+0x24],0x0040920b 32 33 mov word[cs:pgdt_base+0x7c00],39 ;加載gdt 34 lgdt [cs:pgdt_base+0x7c00] 35 36 in al,0x92 ;快速開啟A20 37 or al,0x02 ;是寫入2,不要搞錯了,寫入1就是重啟了 38 out 0x92,al 39 cli ;關(guān)掉BIOS中斷 40 41 mov eax,cr0 42 or eax,0x01 ;設(shè)置PE位 43 mov cr0,eax 44 45 jmp dword 0x0010:flush ;進(jìn)入保護(hù)模式 46 47 [bits 32] 48 flush: 49 mov eax,0x0008 ;選擇4GB的代碼段直接給ds 50 mov ds,eax 51 mov eax,0x0018 52 mov ss,eax ;設(shè)置棧段 53 xor esp,esp 54 55 ;接下來開始讀取內(nèi)核頭部 56 mov esi,core_sector_address 57 mov edi,core_phy_base 58 call read_harddisk_0 59 60 mov eax,[core_phy_base] ;讀取用戶總長度 61 xor edx,edx 62 mov ebx,512 63 div ebx 64 65 cmp edx,0 66 jne @read_last_sector 67 dec eax 68 @read_last_sector: 69 cmp eax,0 70 je @setup 71 mov ecx,eax 72 .read_last: 73 inc esi 74 call read_harddisk_0 75 loop .read_last 76 @setup: 77 mov edi,core_phy_base 78 mov esi,[pgdt_base+0x7c00+0x02] 79 80 ;重新構(gòu)建描述符#1 81 mov eax,[edi+0x04] ;eax線性地址,ebx是下一個線性地址 82 mov ebx,[edi+0x08] 83 sub ebx,eax ;得到段的總長度 84 dec ebx ;段長度-1就是段界限 85 add eax,edi ;得到真正的段的加載地址 86 mov ecx,0x00409800 ;公用例程段 87 call make_gdt_descriptor 88 mov [esi+0x28],eax ;gdt低32位 89 mov [esi+0x2c],edx ;gdt高32位 90 91 ;重新構(gòu)建描述符#2 92 mov eax,[edi+0x08] ;eax線性地址,ebx是下一個線性地址 93 mov ebx,[edi+0x0c] 94 sub ebx,eax ;得到段的總長度 95 dec ebx ;段長度-1就是段界限 96 add eax,edi ;得到真正的段的加載地址 97 mov ecx,0x00409200 ;內(nèi)核數(shù)據(jù)段 98 call make_gdt_descriptor 99 mov [esi+0x30],eax ;gdt低32位 100 mov [esi+0x34],edx ;gdt高32位 101 102 ;重新構(gòu)建描述符#3 103 mov eax,[edi+0x0c] ;eax線性地址,ebx是下一個線性地址 104 mov ebx,[edi+0x00] ;注意這里是程序的總長度(很容易搞錯變成0x10) 105 sub ebx,eax ;得到段的總長度 106 dec ebx ;段長度-1就是段界限 107 add eax,edi ;得到真正的段的加載地址 108 mov ecx,0x00409800 ;內(nèi)核代碼段 109 call make_gdt_descriptor 110 mov [esi+0x38],eax ;gdt低32位 111 mov [esi+0x3c],edx ;gdt高32位 112 113 mov word[pgdt_base+0x7c00],63 ;現(xiàn)在ds指向的就是4GB的內(nèi)存,不可以用cs(會引發(fā)中斷的) 114 lgdt [pgdt_base+0x7c00] 115 116 jmp far [edi+0x10] ;可以這樣跳的原因是因?yàn)樵趦?nèi)核中,選擇子都是固定的,所以不用自己操心 117 118 ;=============================函數(shù)部分================================= 119 read_harddisk_0: ;esi存了28位的硬盤號 120 push ecx 121 122 mov edx,0x1f2 ;讀取一個扇區(qū) 123 mov al,0x01 124 out dx,al 125 126 mov eax,esi ;0~7位,0x1f3端口 127 inc edx 128 out dx,al 129 130 mov al,ah ;8~15位,0x1f4端口 131 inc edx 132 out dx,al 133 134 shr eax,16 ;16-23位,0x1f5端口 135 inc edx 136 out dx,al 137 138 mov al,ah ;24-28位,LBA模式主硬盤 139 inc edx 140 and al,0x0f 141 or al,0xe0 142 out dx,al 143 144 inc edx ;讀命令,0x1f7端口 145 mov al,0x20 146 out dx,al 147 148 .wait: 149 in al,dx 150 and al,0x88 151 cmp al,0x08 152 jne .wait 153 154 mov dx,0x1f0 155 mov ecx,256 156 .read: 157 in ax,dx 158 mov [edi],ax 159 add edi,2 160 loop .read 161 162 pop ecx 163 164 ret 165 ;---------------------------------------------------------------------- 166 make_gdt_descriptor: 167 ;eax:線性基地址 168 ;ebx:段界限 169 ;ecx:屬性 170 mov edx,eax 171 and edx,0xffff0000 ;得到線性基地址的16-31位 172 rol edx,8 173 bswap edx ;強(qiáng)行把0-7位和16-23位互換 174 or edx,ecx ;裝載屬性 175 176 shl eax,16 177 or ax,bx ;配好段界限低16位 178 179 and ebx,0x000f0000 180 or edx,ebx ;裝載段界限的16-19位 181 182 ret 183 ;====================================================================== 184 pgdt_base dw 0 185 dd 0x00007e00 ;GDT的物理地址 186 ;====================================================================== 187 times 510-($-$$) db 0 188 dw 0xaa55 1 ;===============================內(nèi)核程序================================= 2 ;定義內(nèi)核所要用到的選擇子 3 All_4GB_Segment equ 0x0008 ;4GB的全內(nèi)存區(qū)域 4 Stack_Segement equ 0x0018 ;內(nèi)核棧區(qū) 5 Print_Segement equ 0x0020 ;顯存映射區(qū) 6 Sys_Routine_Segement equ 0x0028 ;公用例程段 7 Core_Data_Segement equ 0x0030 ;內(nèi)核數(shù)據(jù)區(qū) 8 Core_Code_Segement equ 0x0038 ;內(nèi)核代碼段 9 ;---------------------------------------------------------------- 10 User_Program_Address equ 50 ;用戶程序所在邏輯扇區(qū) 11 ;=============================內(nèi)核程序頭部=============================== 12 SECTION header vstart=0 13 Program_Length dd Program_end ;內(nèi)核總長度 14 Sys_Routine_Seg dd section.Sys_Routine.start ;公用例程段線性地址 15 Core_Data_Seg dd section.Core_Data.start ;內(nèi)核數(shù)據(jù)區(qū)線性地址 16 Core_Code_Seg dd section.Core_Code.start ;內(nèi)核代碼區(qū)線性地址 17 Code_Entry dd start ;注意偏移地址一定是32位的 18 dw Core_Code_Segement 19 ;---------------------------------------------------------------- 20 [bits 32] 21 ;========================================================================= 22 ;============================公用例程區(qū)=================================== 23 ;========================================================================= 24 SECTION Sys_Routine align=16 vstart=0 25 ReadHarddisk: ;esi:28位磁盤號 26 ;ebx:偏移地址 27 pushad 28 29 mov dx,0x1f2 30 mov al,0x01 ;讀一個扇區(qū) 31 out dx,al 32 33 inc edx ;0-7位 34 mov eax,esi 35 out dx,al 36 37 inc edx ;8-15位 38 mov al,ah 39 out dx,al 40 41 inc edx ;16-23位 42 shr eax,16 43 out dx,al 44 45 inc edx ;24-28位,主硬盤,LBA模式 46 mov al,ah 47 and al,0x0f 48 or al,0xe0 49 out dx,al 50 51 inc edx 52 mov al,0x20 53 out dx,al 54 55 _wait: 56 in al,dx 57 and al,0x88 58 cmp al,0x08 59 jne _wait 60 61 mov dx,0x1f0 62 mov ecx,256 63 64 _read: 65 in ax,dx 66 mov [ebx],ax 67 add ebx,2 68 loop _read 69 70 popad 71 retf 72 ;---------------------------------------------------------------- 73 put_string: ;ebx:偏移地址 74 pushad 75 76 _print: 77 mov cl,[ebx] 78 cmp cl,0 79 je _exit 80 call put_char 81 inc ebx 82 jmp _print 83 _exit: 84 popad 85 retf ;段間返回 86 ;-------------------------------------------------------------- 87 put_char: ;cl就是要顯示的字符 88 push ebx 89 push es 90 push ds 91 92 mov dx,0x3d4 93 mov al,0x0e ;高8位 94 out dx,al 95 mov dx,0x3d5 96 in al,dx 97 mov ah,al ;先把高8位存起來 98 mov dx,0x3d4 99 mov al,0x0f ;低8位 100 out dx,al 101 mov dx,0x3d5 102 in al,dx ;現(xiàn)在ax就是當(dāng)前光標(biāo)的位置 103 104 _judge: 105 cmp cl,0x0a 106 je _set_0x0a 107 cmp cl,0x0d 108 je _set_0x0d 109 _print_visible: 110 mov bx,ax 111 mov eax,Print_Segement 112 mov es,eax 113 shl bx,1 ;注意這里一定要把ebx變成原來的兩倍,實(shí)際位置是光標(biāo)位置的兩倍 114 mov [es:bx],cl ;注意這里是屏幕! 115 mov byte[es:bx+1],0x07 116 add bx,2 117 shr bx,1 118 jmp _roll_screen 119 _set_0x0d: ;回車 120 mov bl,80 121 div bl 122 mul bl 123 mov bx,ax 124 jmp _set_cursor 125 _set_0x0a: ;換行 126 mov bx,ax 127 add bx,80 128 jmp _roll_screen 129 _roll_screen: 130 cmp bx,2000 131 jl _set_cursor 132 mov eax,Print_Segement 133 mov ds,eax 134 mov es,eax 135 136 cld 137 mov edi,0x00 138 mov esi,0xa0 139 mov ecx,1920 140 rep movsw 141 _cls: 142 mov bx,3840 143 mov ecx,80 144 _print_blank: 145 mov word[es:bx],0x0720 146 add bx,2 147 loop _print_blank 148 mov bx,1920 ;別總是忘了光標(biāo)的位置! 149 _set_cursor: ;改變后的光標(biāo)位置在bx上 150 mov dx,0x3d4 151 mov al,0x0f ;低8位 152 out dx,al 153 154 mov al,bl 155 mov dx,0x3d5 156 out dx,al 157 158 mov dx,0x3d4 159 mov al,0x0e ;高8位 160 out dx,al 161 162 mov al,bh 163 mov dx,0x3d5 164 out dx,al 165 166 pop ds 167 pop es 168 pop ebx 169 ret 170 ;---------------------------------------------------------------- 171 allocate_memory: ;簡易內(nèi)存分配策略 172 ;輸入ecx:想要分配的總字節(jié)數(shù) 173 ;輸出ecx:分配的線性基地址 174 push ds 175 push eax 176 push ebx 177 178 mov eax,Core_Data_Segement 179 mov ds,eax 180 mov eax,[ram_alloc] 181 mov edx,eax ;edx暫存一下eax 182 add eax,ecx 183 184 cmp eax,edx ;發(fā)現(xiàn)新分配的現(xiàn)地址比原來的還小,說明已經(jīng)溢出 185 jge _alloc 186 mov ebx,mem_alloc_fail 187 call Sys_Routine_Segement:put_string 188 mov ecx,0 ;分配為0說明已經(jīng)分配失敗 189 jmp _exit1 190 _alloc: 191 192 mov ebx,eax 193 and ebx,0xfffffffc 194 add ebx,4 ;強(qiáng)行向上取整 195 test eax,0x00000003 196 cmovnz eax,ebx 197 mov ecx,[ram_alloc] ;要返回要分配的初始地址 198 mov [ram_alloc],eax ;下一次分配的線性基地址 199 add [ram_recycled],eax 200 sub [ram_recycled],ecx ;記錄大小 201 202 _exit1: 203 pop ebx 204 pop eax 205 pop ds 206 207 retf 208 ;---------------------------------------------------------------- 209 recycled_memory_and_gdt: 210 mov eax,[ram_recycled] 211 sub [ram_alloc],eax 212 mov dword[ram_recycled],0 ;因?yàn)槲覀冞€沒學(xué)到多任務(wù),先這樣簡單地清零 213 214 sgdt [pgdt_base_tmp] 215 sub word[pgdt_base_tmp],32 ;應(yīng)用程序的4個段全部減掉 216 lgdt [pgdt_base_tmp] ;重新加載內(nèi)核 217 retf 218 ;---------------------------------------------------------------- 219 PrintDword: ;顯示edx內(nèi)容的一個調(diào)試函數(shù) 220 pushad 221 push ds 222 223 mov eax,Core_Data_Segement 224 mov ds,eax 225 226 mov ebx,bin_hex 227 mov ecx,8 228 229 _query: 230 rol edx,4 231 mov eax,edx 232 and eax,0x0000000f 233 xlat 234 235 push ecx 236 mov cl,al 237 call put_char 238 pop ecx 239 240 loop _query 241 242 pop ds 243 popad 244 245 retf 246 ;---------------------------------------------------------------- 247 Make_Descriptor: ;構(gòu)造描述符 248 ;輸入: 249 ;eax:線性基地址 250 ;ebx:段界限 251 ;ecx:屬性 252 ;輸出: 253 ;eax:描述符低32位 254 ;edx:描述符高32位 255 mov edx,eax 256 and edx,0xffff0000 257 rol edx,8 258 bswap edx 259 or edx,ecx 260 261 shl eax,16 262 or ax,bx 263 and ebx,0x000f0000 264 or edx,ebx 265 retf 266 ;---------------------------------------------------------------- 267 Set_New_GDT: ;裝載新的描述符 268 ;輸入:edx:eax描述符 269 ;輸出:cx選擇子 270 push ds 271 push es 272 273 mov ebx,Core_Data_Segement 274 mov ds,ebx 275 276 mov ebx,All_4GB_Segment 277 mov es,ebx 278 279 sgdt [pgdt_base_tmp] 280 281 movzx ebx,word[pgdt_base_tmp] 282 inc bx ;注意這里要一定是inc bx而不是inc ebx,因?yàn)間dt段地址初始化是0xffff的 283 ;要用到回繞特性 284 add ebx,[pgdt_base_tmp+0x02] ;得到pgdt的線性基地址 285 286 mov [es:ebx],eax 287 mov [es:ebx+0x04],edx ;裝載新的gdt符 288 ;裝載描述符要裝載到實(shí)際位置上 289 290 add word[pgdt_base_tmp],8 ;給gdt的段界限加上8(字節(jié)) 291 292 lgdt [pgdt_base_tmp] ;加載gdt到gdtr的位置和實(shí)際表的位置無關(guān) 293 294 mov ax,[pgdt_base_tmp] ;得到段界限 295 xor dx,dx 296 mov bx,8 ;得到gdt大小 297 div bx 298 mov cx,ax 299 shl cx,3 ;得到選擇子,ti=0(全局描述符),rpl=0(申請?zhí)貦?quán)0級) 300 301 pop es 302 pop ds 303 retf 304 ;========================================================================= 305 ;===========================內(nèi)核數(shù)據(jù)區(qū)==================================== 306 ;========================================================================= 307 SECTION Core_Data align=16 vstart=0 308 ;------------------------------------------------------------------------------- 309 pgdt_base_tmp: dw 0 ;這一章的用戶程序都是從GDT中加載的 310 dd 0 311 312 ram_alloc: dd 0x00100000 ;下次分配內(nèi)存時的起始地址(直接暴力從0x00100000開始分配了) 313 ram_recycled dd 0 ;這里儲存程序?qū)嶋H用的大小 314 salt: 315 salt_1: db '@Printf' ;@Printf函數(shù)(公用例程) 316 times 256-($-salt_1) db 0 317 dd put_string 318 dw Sys_Routine_Segement 319 320 salt_2: db '@ReadHarddisk' ;@ReadHarddisk函數(shù)(公用例程) 321 times 256-($-salt_2) db 0 322 dd ReadHarddisk 323 dw Sys_Routine_Segement 324 325 salt_3: db '@PrintDwordAsHexString' ;@PrintDwordAsHexString函數(shù)(公用例程) 326 times 256-($-salt_3) db 0 327 dd PrintDword 328 dw Sys_Routine_Segement 329 330 salt_4: db '@TerminateProgram' ;@TerminateProgram函數(shù)(內(nèi)核例程) 331 times 256-($-salt_4) db 0 332 dd _return_point 333 dw Core_Code_Segement 334 335 salt_length: equ $-salt_4 336 salt_items_sum equ ($-salt)/salt_length ;得到項(xiàng)目總數(shù) 337 338 message_1 db ' If you seen this message,that means we ' 339 db 'are now in protect mode,and the system ' 340 db 'core is loaded,and the video display ' 341 db 'routine works perfectly.',0x0d,0x0a,0 342 343 message_5 db ' Loading user program...',0 344 345 do_status db 'Done.',0x0d,0x0a,0 346 347 message_6 db 0x0d,0x0a,0x0d,0x0a,0x0d,0x0a 348 db ' User program terminated,control returned.',0 349 message_7 db 0x0d,0x0a,0x0d,0x0a 350 db ' We have been backed to kernel.',0 351 message_8 db 0x0d,0x0a 352 db ' The GDT and memory have benn recycled.',0 353 354 bin_hex db '0123456789ABCDEF' 355 ;put_hex_dword子過程用的查找表 356 core_buf times 2048 db 0 ;內(nèi)核用的緩沖區(qū)(2049個字節(jié)(2MB)) 357 358 esp_pointer dd 0 ;內(nèi)核用來臨時保存自己的棧指針 359 360 cpu_brnd0 db 0x0d,0x0a,' ',0 361 cpu_brand times 52 db 0 362 cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0 363 mem_alloc_fail db 'The Program is too large to load' 364 ;========================================================================= 365 ;===========================內(nèi)核代碼區(qū)==================================== 366 ;========================================================================= 367 SECTION Core_Code align=16 vstart=0 368 load_program: ;輸入esi:磁盤號 369 ;輸出ax: 用戶程序頭部選擇子 370 push esi 371 push ds 372 push es 373 374 mov eax,Core_Data_Segement 375 mov ds,eax ;切換到內(nèi)核數(shù)據(jù)段 376 377 mov ebx,core_buf ;ebx要在內(nèi)核數(shù)據(jù)緩沖區(qū)(先讀取頭部在緩沖區(qū),esi已經(jīng)是有扇區(qū)號了) 378 call Sys_Routine_Segement:ReadHarddisk 379 380 mov eax,[core_buf] ;讀取用戶程序長度 381 382 mov ebx,eax ;給ebx一個副本 383 and ebx,0xfffffe00 ;清空低9位(強(qiáng)制對齊512) 384 add ebx,512 ;加上512 385 test eax,0x000001ff 386 cmovnz eax,ebx ;低9位不為0則使用向上取整的結(jié)果 387 388 mov ecx,eax ;eax是整個程序的向上取整的大小 389 call Sys_Routine_Segement:allocate_memory ;先分配內(nèi)存給整個程序,再分配內(nèi)存給棧區(qū) 390 mov ebx,ecx ;這個才是真正的程要用到的線性基地址 391 392 push ebx ;ebx就是用戶程序加載到內(nèi)存的地址 393 xor edx,edx 394 mov ecx,512 ;千萬不要改掉ebx 395 div ecx 396 mov ecx,eax 397 398 mov eax,All_4GB_Segment ;切換到4GB段區(qū)域(平坦模式) 399 mov ds,eax 400 401 _loop_read: 402 call Sys_Routine_Segement:ReadHarddisk ;esi還是User_Program_Address 403 inc esi 404 add ebx,512 405 loop _loop_read 406 407 ;加載用戶程序頭部段(下面的所有段的地址都要轉(zhuǎn)化為選擇子) 408 pop edi ;程序被裝載的基地址 409 mov eax,edi 410 mov ebx,[edi+0x04] ;用戶程序頭部的長度 411 dec ebx ;段界限 412 mov ecx,0x00409200 413 call Sys_Routine_Segement:Make_Descriptor 414 call Sys_Routine_Segement:Set_New_GDT 415 mov [edi+0x04],cx ;放入選擇子 416 417 ;加載用戶程序代碼段 418 mov eax,edi 419 add eax,[edi+0x14] ;別忘記重定位了 420 mov ebx,[edi+0x18] ;用戶程序代碼段的長度 421 dec ebx ;段界限 422 mov ecx,0x00409800 423 call Sys_Routine_Segement:Make_Descriptor 424 call Sys_Routine_Segement:Set_New_GDT 425 mov [edi+0x14],cx ;放入選擇子 426 427 ;加載用戶程序數(shù)據(jù)段 428 mov eax,edi 429 add eax,[edi+0x1c] ;別忘記重定位了 430 mov ebx,[edi+0x20] ;用戶程序數(shù)據(jù)段的長度 431 dec ebx ;段界限 432 mov ecx,0x00409200 433 call Sys_Routine_Segement:Make_Descriptor 434 call Sys_Routine_Segement:Set_New_GDT 435 mov [edi+0x1c],cx ;放入選擇子 436 437 ;加載用戶程序棧段(用戶程序給出的建議大小) 438 mov ecx,[edi+0x0c] ;確定棧段的建議大小 439 mov ebx,0x000fffff 440 sub ebx,ecx ;這就是段界限了 441 mov eax,4096 ;4KB 442 mul ecx 443 add [ram_recycled],eax ;加上分配的內(nèi)存 444 445 mov ecx,eax ;這個時候eax是大小 446 call Sys_Routine_Segement:allocate_memory ;分到內(nèi)存 447 add eax,ecx ;eax線性基地址,ebx段界限 448 mov ecx,0x00c09600 ;4KB粒度向下拓展數(shù)據(jù)段 449 call Sys_Routine_Segement:Make_Descriptor 450 call Sys_Routine_Segement:Set_New_GDT 451 mov [edi+0x08],cx 452 453 ;現(xiàn)在開始重定位API符號表 454 ;--------------------------------------------------------------------- 455 mov eax,[edi+0x04] ;先設(shè)es再ds,不要搞反了,現(xiàn)在es的0x04這個地方是頭部的選擇子 456 mov es,eax 457 mov eax,Core_Data_Segement 458 mov ds,eax 459 460 cld 461 mov ecx,[es:0x24] ;得到用戶程序符號表的條數(shù) 462 mov edi,0x28 ;用戶符號表的偏移地址是0x28 463 464 _loop_U_SALT: 465 push edi 466 push ecx 467 468 mov ecx,salt_items_sum 469 mov esi,salt 470 471 _loop_C_SALT: 472 push edi 473 push esi 474 push ecx 475 476 mov ecx,64 ;比較256個字節(jié) 477 repe cmpsd 478 jne _re_match ;如果成功匹配,那么esi和edi剛好會在數(shù)據(jù)區(qū)之后的 479 480 mov eax,[esi] ;偏移地址 481 mov [es:edi-256],eax ;把偏移地址填入用戶程序的符號區(qū) 482 mov ax,[esi+0x04] ;段的選擇子 483 mov [es:edi-252],ax ;把段的選擇子填入用戶程序的段選擇區(qū) 484 485 _re_match: 486 pop ecx 487 pop esi 488 add esi,salt_length 489 pop edi 490 loop _loop_C_SALT 491 492 pop ecx 493 pop edi 494 add edi,256 495 loop _loop_U_SALT 496 ;--------------------------------------------------------------------- 497 mov ax,[es:0x04] ;把頭部的段選擇子給ax 498 499 pop es 500 pop ds 501 pop esi 502 ret 503 start: 504 mov eax,Core_Data_Segement 505 mov ds,eax 506 507 mov ebx,message_1 508 call Sys_Routine_Segement:put_string 509 510 mov eax,0 511 cpuid 512 cmp eax,0x80000004 ;判斷是否有0x80000002-0x80000004功能 513 jl _@load 514 515 ;顯示處理器品牌信息,從80486的后期版本開始引入 516 mov eax,0x80000002 517 cpuid 518 mov [cpu_brand+0x00],eax 519 mov [cpu_brand+0x04],ebx 520 mov [cpu_brand+0x08],ecx 521 mov [cpu_brand+0x0c],edx 522 523 mov eax,0x80000003 524 cpuid 525 mov [cpu_brand+0x10],eax 526 mov [cpu_brand+0x14],ebx 527 mov [cpu_brand+0x18],ecx 528 mov [cpu_brand+0x1c],edx 529 530 mov eax,0x80000004 531 cpuid 532 mov [cpu_brand+0x20],eax 533 mov [cpu_brand+0x24],ebx 534 mov [cpu_brand+0x28],ecx 535 mov [cpu_brand+0x2c],edx 536 537 mov ebx,cpu_brnd0 538 call Sys_Routine_Segement:put_string 539 mov ebx,cpu_brand 540 call Sys_Routine_Segement:put_string 541 mov ebx,cpu_brnd1 542 call Sys_Routine_Segement:put_string 543 544 _@load: 545 mov ebx,message_5 546 call Sys_Routine_Segement:put_string 547 mov esi,User_Program_Address 548 call load_program 549 550 mov ebx,do_status 551 call Sys_Routine_Segement:put_string 552 553 mov [esp_pointer],esp ;臨時保存一下棧指針 554 mov ds,ax ;使ds指向用戶程序頭部 555 556 jmp far [0x10] 557 558 _return_point: 559 560 mov eax,Core_Data_Segement 561 mov ds,eax 562 mov eax,Stack_Segement 563 mov ss,eax ;重新設(shè)置數(shù)據(jù)段和棧段 564 mov esp,[esp_pointer] 565 mov ebx,message_7 566 call Sys_Routine_Segement:put_string 567 568 call Sys_Routine_Segement:recycled_memory_and_gdt 569 mov ecx,[ram_alloc] 570 571 mov ebx,message_8 572 call Sys_Routine_Segement:put_string 573 cli 574 hlt 575 ;========================================================================= 576 SECTION core_trail 577 ;---------------------------------------------------------------- 578 Program_end: 1 ;==============================用戶程序======================================= 2 SECTION header vstart=0 3 4 program_length dd program_end ;程序總長度#0x00 5 6 head_len dd header_end ;程序頭部的長度#0x04 7 8 stack_seg dd 0 ;用于接收堆棧段選擇子#0x08 9 stack_len dd 1 ;程序建議的堆棧大小#0x0c 10 ;以4KB為單位 11 12 prgentry dd start ;程序入口#0x10 13 code_seg dd section.code.start ;代碼段位置#0x14 14 code_len dd code_end ;代碼段長度#0x18 15 16 data_seg dd section.data.start ;數(shù)據(jù)段位置#0x1c 17 data_len dd data_end ;數(shù)據(jù)段長度#0x20 18 ;------------------------------------------------------------------------------- 19 ;符號地址檢索表 20 salt_items dd (header_end-salt)/256 ;#0x24 21 22 salt: ;#0x28 23 Printf: db '@Printf' 24 times 256-($-Printf) db 0 25 26 TerminateProgram:db '@TerminateProgram' 27 times 256-($-TerminateProgram) db 0 28 29 ReadHarddisk: db '@ReadHarddisk' 30 times 256-($-ReadHarddisk) db 0 31 32 header_end: 33 ;=============================================================================== 34 SECTION data align=16 vstart=0 35 36 buffer times 1024 db 0 ;緩沖區(qū) 37 38 message_1 db 0x0d,0x0a,0x0d,0x0a 39 db '**********User program is runing**********' 40 db 0x0d,0x0a,0 41 message_2 db ' Disk data:',0x0d,0x0a,0 42 43 data_end: 44 45 ;=============================================================================== 46 [bits 32] 47 ;=============================================================================== 48 SECTION code align=16 vstart=0 49 start: 50 User_Data_File equ 100 ;數(shù)據(jù)文件存放地點(diǎn) 51 mov eax,ds 52 mov fs,eax 53 54 mov eax,[stack_seg] 55 mov ss,eax 56 mov esp,0 57 58 mov eax,[data_seg] 59 mov ds,eax 60 61 mov ebx,message_1 62 call far [fs:Printf] 63 64 mov esi,User_Data_File 65 mov ebx,buffer ;緩沖區(qū)偏移地址 66 call far [fs:ReadHarddisk] ;相當(dāng)于調(diào)用函數(shù) 67 68 mov ebx,message_2 69 call far [fs:Printf] 70 71 mov ebx,buffer 72 call far [fs:Printf] 73 74 jmp far [fs:TerminateProgram] ;將控制權(quán)返回到系統(tǒng) 75 76 code_end: 77 78 ;=============================================================================== 79 SECTION trail 80 ;------------------------------------------------------------------------------- 81 program_end:2. 課后習(xí)題
?????? 其實(shí)這一章的課后習(xí)題很無聊的,就是把棧段那里改一下就好了,用戶程序按照第八章那樣自己填一個區(qū)域。
? ?
1 ;加載用戶程序棧段 2 mov eax,edi 3 add eax,[edi+0x08] 4 mov ebx,[edi+0x0c] ;用戶程序棧段的長度 5 add eax,ebx ;得到棧段的線性基地址 6 mov edx,0xffffffff 7 sub edx,ebx 8 mov ebx,edx ;得到段界限 9 10 mov ecx,0x00409600 11 call Sys_Routine_Segement:Make_Descriptor 12 call Sys_Routine_Segement:Set_New_GDT 13 mov [edi+0x08],cx ;放入選擇子 1 ;==============================用戶程序======================================= 2 SECTION header vstart=0 3 4 program_length dd program_end ;程序總長度#0x00 5 6 head_len dd header_end ;程序頭部的長度#0x04 7 8 stack_seg dd section.stack.start ;棧段位置#0x08 9 stack_len dd stack_end ;棧段長度#0x0c 10 11 12 prgentry dd start ;程序入口#0x10 13 code_seg dd section.code.start ;代碼段位置#0x14 14 code_len dd code_end ;代碼段長度#0x18 15 16 data_seg dd section.data.start ;數(shù)據(jù)段位置#0x1c 17 data_len dd data_end ;數(shù)據(jù)段長度#0x20 18 ;------------------------------------------------------------------------------- 19 ;符號地址檢索表 20 salt_items dd (header_end-salt)/256 ;#0x24 21 22 salt: ;#0x28 23 Printf: db '@Printf' 24 times 256-($-Printf) db 0 25 26 TerminateProgram:db '@TerminateProgram' 27 times 256-($-TerminateProgram) db 0 28 29 ReadHarddisk: db '@ReadHarddisk' 30 times 256-($-ReadHarddisk) db 0 31 32 header_end: 33 ;=============================================================================== 34 SECTION data align=16 vstart=0 35 36 buffer times 1024 db 0 ;緩沖區(qū) 37 38 message_1 db 0x0d,0x0a,0x0d,0x0a 39 db '**********User program is runing**********' 40 db 0x0d,0x0a,0 41 message_2 db ' Disk data:',0x0d,0x0a,0 42 43 data_end: 44 45 ;=============================================================================== 46 [bits 32] 47 ;=============================================================================== 48 SECTION code align=16 vstart=0 49 start: 50 User_Data_File equ 100 ;數(shù)據(jù)文件存放地點(diǎn) 51 mov eax,ds 52 mov fs,eax 53 54 mov eax,[stack_seg] 55 mov ss,eax 56 mov esp,0 57 58 mov eax,[data_seg] 59 mov ds,eax 60 61 mov ebx,message_1 62 call far [fs:Printf] 63 64 mov esi,User_Data_File 65 mov ebx,buffer ;緩沖區(qū)偏移地址 66 call far [fs:ReadHarddisk] ;相當(dāng)于調(diào)用函數(shù) 67 68 mov ebx,message_2 69 call far [fs:Printf] 70 71 mov ebx,buffer 72 call far [fs:Printf] 73 74 jmp far [fs:TerminateProgram] ;將控制權(quán)返回到系統(tǒng) 75 76 code_end: 77 ;=============================================================================== 78 SECTION stack align=16 vstart=0 79 times 4096 db 0 80 stack_end: 81 ;=============================================================================== 82 SECTION trail 83 ;------------------------------------------------------------------------------- 84 program_end:?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/Philip-Tell-Truth/p/5226696.html
總結(jié)
以上是生活随笔為你收集整理的ASM:《X86汇编语言-从实模式到保护模式》第13章:保护模式下内核的加载,程序的动态加载和执行...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022年冷链物流行业分析
- 下一篇: 7年级计算机说课稿,七年级信息技术说课稿