zoukankan      html  css  js  c++  java
  • ASM:《X86汇编语言-从实模式到保护模式》第13章:保护模式下内核的加载,程序的动态加载和执行

    ★PART1:32位保护模式下内核简易模型

    1. 内核的结构,功能和加载

           每个内核的主引导程序都会有所不同,因为内核都会有不同的结构。有时候主引导程序的一些段和内核段是可以共用的(事实上加载完内核以后就不需要主引导程序了),和加载一般的用户程序一样,主引导程序也是需要从硬盘中读取程序到指定的内存空间中。

           同时,作为一个内核,也是一个程序,而且是一个具有管理全局的能力的程序,应该有固定的段,一般来说,内核应该包括以下几个部分:

           1. 公用例程段(实现API功能)

           2. 内核数据区(用于预读一些数据和一些内核内置的保留的内容)

           3. 内核代码区(用于执行内核自己的代码…)

           PS:上述段都应该具有特权0级,特权级在教材的14章讲述

           在主引导程序中,为了加载内核,首先应该在GDT中加载内核的所有的描述符。但是内核的描述符是不确定的(因为段界限和段的线性基地址不知道),所以要根据内核头部的信息来确定,内核的头部和普通的程序是一样的,也是应该把各个段的信息和入口点的相对地址列出来,同时还需要具有段的选择子(因为内核是固定的,选择子也应该固定)。然后主引导程序构建这些段的描述符(需要重定位,不过不需要再把段信息写入内核头部了,因为选择子有了),并且把描述符写入GDT并且重新加载GDT即可(主要是刷新GDT的大小)。

      

           那怎么根据内核所给出的信息构建内核的描述符呢?教材给出了一种方法。(edi的内容是内核头部的地址)

                

      

           其实原理很简单,就是给出需要加载的段的属性(因为加载什么段我们是知道的),然后只要填充好描述符的高32位和低32位就完了,教材这里用到了一个bswap(bswap r32)命令,简单来说这个命令就是将以某个寄存器中8位为一个段的首尾交换,如图:

     

    2. 保护模式下的用户程序的重定位和加载

           首先要明白一个事情,在保护模式下(特别是在有权限管理的情况下),程序是很难进行自我加载的,必须通过内核来加载。当然了,用户程序必须符合固定的格式,才能被内核识别和加载。一般来说,内核需要给用户程序提供3个段,这三个段一般是:

                         1. 代码段,

                         2. 数据段(这里应该有两个区域:用户程序头部和文字常量区),

                         3. 栈段,其中栈段要根据用户程序的建议的栈段大小来定(一般不建议用户自己创建栈段)。

                         (正常来讲还要有堆和BSS段,但是先不搞那么复杂)

           内核从硬盘读取了用户程序并加载到相应位置的时候,就读取用户的程序头。我们知道,内核有公用程序段,是专门用来给用户程序提供公用例程的,这就是现代操作系统的API(Application Programming Interface),更直观来说就是就是一堆库函数名(比如C中的scanf,printf…)。早期的系统,API是通过中断号的方式公布的(也就是要通过软中断进入),现在常用方法是使用符号名。正常来讲,一个现代的操作系统应该是具有一个专门的链接器(Linker)来构建文件头的。教材用一个比较原始的方式来构建文件头了,其实这只是一个简单的字符串的匹配的过程而已。

    (内核数据区的符号表的样貌)

           在我们的规定中,用户程序的符号表固定是256个字符,同时每个符号还需要预留一个区域来保存符号对应过程的选择子(本章过程的调用是通过GDT进行的,下一章将会介绍调用门)。两个字符串的比较可以使用cmpsb(字节比较),cmpsw(字比较),cmpsd(双字比较),在16为模式下,源字符串的首地址由DS:SI指定,目的字符串的首地址由ES:DI指定,在32位模式下,则分别是DS:ESI和ES:EDI,在处理器内部,cmps指令的操作是把两个操作数相减,然后根据结果设置标志寄存器中的标志位。

           单纯的cmps(指令族)只比较一次,需要通过rep前缀来不断驱动(直到ecx为0),但是这里不能单纯地使用rep指令,否则就无法比较了

     

           重定位符号表后,我们就要对用户程序的段进行重定位和内存的分配,当然了,理论上在读取用户头部的时候就应该开辟一个内存放置用户程序,但是内核的做法是,先预读头部到内核数据区,然后再给用户程序分配内存,在现代操作系统中,内存分配是一个很重要的工作。内存管理程序不仅要把内存分块管理以便给应用程序分配内存,还要负责回收,还要进行虚拟内存管理的工作,非常复杂。这里教材用的是一个非常简单的分配demo,没有检测内存是否越界的功能,而且也没有内存回收。我往上加了一点东西。

          

           接下来就是段的重定位和描述符的创建了,当然这一章没有讲特权级,所以教材呢就直接把所有的程序都按特权0级的权限加载了。当然这是一种很不好的做法,教材在14章会介绍如何用特权3级来加载程序。其实也不难,懂怎么加载内核就知道怎么怎么加载用户程序了,主要是栈段的分配要用到内存分配函数而已。

      

      

           这里要注意的地方是Set_New_GDT这个过程,要在GDT中安装描述符,必须知道他的物理地址和大小,要知道这些消息,可以用sdgt指令(Store Global Descriptor Table Regiser)它用于将GDT军训器的基地址和边界信息保存到指定的内存位置,sgdt的指令格式是sgdt m/48,这个指令不会影响任何标志位。另外还有一个新的指令movzx

           movzx是一个带零拓展的传送(Move With Zero-Extend)指令格式为(其实就是相当于先xor一下16/32位寄存器再写入低位的寄存器)

    movzx r16,r/m8

    movzx r32,r/m8

    movzx r32,r/m16

           注意movzx的指令目的操作数只能是16位或者是32位的通用寄存器,源操作数只能是8位或者16位的寄存器或者内存地址,而且目的操作数和源操作数的大小是不一样的。和movzx指令差不多的指令是movsx指令,他是带符号的传送,这个指令就不像movzx一样只会拓展0了,而是根据数的最高位来拓展。

           GDTR的界限部分在初始化的时候会初始化为0xFFFF,当+1时,如果看成16位数,则会是0x0000,如果看成32位数,则会变成0x00010000,为了避免不必要的麻烦,直接写成inc bx,那这样的话开始填充描述符的时候就不是从0x00010000这个偏移地址开始了,而是0x00000000开始了。

           因为应用程序给出的都是栈的建议大小,所以一般都是直接给出的是建议大小,所以我们直接按建议大小来乘以4KB的大小来分配内存,要注意的是栈段是向下拓展的,高地址才是栈段的线性基地址。      

    2. 用户程序的执行

           一旦加载了用户程序,那么我们就可以直接直接一个跳转指令跳转到用户程序了

      

           此时ds应该指向用户程序头部。0x10刚好是偏移地址(32位)+选择子(16位),接下来就是在用户程序执行一系列操作了,因为我们已经在用户程序填入了公用例程段的选择子,所以我们直接用fs指向用户头部,然后执行这些公用例程就可以了。

     

           TerminateProgram是返回内核的公用例程,返回内核后就可以回收用户程序用的内存和创建的GDT了。

          

           方法简单粗暴,当然学了14,15章以后我们就能学习到正确的任务切换方法了。

           最后,教材还给出一种调试程序的方法,其实个人感觉没有什么用,还不如直接在bochs看呢。应该就是为了讲xlat这个查表指令而写的,该指令要求事先在DS:(E)BX出定义一个用于转换编码的表格,在16位下使用bx,在32位下使用ebx。指令执行的时候,处理器访问该表格,用AL寄存器作为偏移量,从表格中取出一个字节,传回AL寄存器。教材上写了一个调试函数。

          

       每次将edx移动4次,总共需要移动8次,而且每次只取4位,Core_Data_Segement段的bin_hex中有16进制的对照表。Xlat不影响任何标志位。

    ★PART2:13章的代码

    1. 源代码:

      1 ;========================保护模式主引导扇区代码========================
      2         core_phy_base:         equ 0x00040000        ;内核加载地址
      3         core_sector_address: equ 0x00000001        ;内核所在扇区
      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位置进行操作
     15         mov ebx,edx                                ;别忘了还有可能出现偏移地址
     16         
     17         ;---------------------描述符#0---------------------
     18         mov dword [ebx+0x00],0x00000000            ;空描述符
     19         mov dword [ebx+0x04],0x00000000
     20         ;---------------------描述符#1---------------------
     21         mov dword [ebx+0x08],0x0000ffff            ;4GB向上拓展数据段
     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                                        ;关掉BIOS中断
     40         
     41         mov eax,cr0
     42         or eax,0x01                                ;设置PE位
     43         mov cr0,eax
     44         
     45         jmp dword 0x0010:flush                    ;进入保护模式
     46         
     47         [bits 32]
     48     flush:
     49         mov eax,0x0008                            ;选择4GB的代码段直接给ds
     50         mov ds,eax
     51         mov eax,0x0018
     52         mov ss,eax                                ;设置栈段
     53         xor esp,esp                             
     54         
     55         ;接下来开始读取内核头部
     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             ;重新构建描述符#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             ;重新构建描述符#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                    ;内核数据段
     98             call make_gdt_descriptor
     99             mov [esi+0x30],eax                    ;gdt低32位
    100             mov [esi+0x34],edx                    ;gdt高32位
    101             
    102             ;重新构建描述符#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                    ;内核代码段
    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            ;现在ds指向的就是4GB的内存,不可以用cs(会引发中断的)
    114             lgdt [pgdt_base+0x7c00]
    115             
    116             jmp far [edi+0x10]                    ;可以这样跳的原因是因为在内核中,选择子都是固定的,所以不用自己操心
    117 
    118 ;=============================函数部分=================================
    119 read_harddisk_0:                                ;esi存了28位的硬盘号
    120         push ecx
    121         
    122         mov edx,0x1f2                            ;读取一个扇区
    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                                ;强行把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 ;===============================内核程序=================================
      2         ;定义内核所要用到的选择子
      3         All_4GB_Segment         equ 0x0008        ;4GB的全内存区域
      4         Stack_Segement             equ 0x0018        ;内核栈区
      5         Print_Segement            equ 0x0020        ;显存映射区
      6         Sys_Routine_Segement     equ 0x0028        ;公用例程段
      7         Core_Data_Segement        equ 0x0030        ;内核数据区
      8         Core_Code_Segement        equ 0x0038        ;内核代码段
      9         ;----------------------------------------------------------------
     10         User_Program_Address    equ 50            ;用户程序所在逻辑扇区
     11 ;=============================内核程序头部===============================
     12 SECTION header vstart=0
     13         Program_Length             dd    Program_end                    ;内核总长度
     14         Sys_Routine_Seg         dd  section.Sys_Routine.start    ;公用例程段线性地址
     15         Core_Data_Seg             dd  section.Core_Data.start        ;内核数据区线性地址
     16         Core_Code_Seg             dd  section.Core_Code.start        ;内核代码区线性地址
     17         Code_Entry                dd    start                        ;注意偏移地址一定是32位的
     18                                 dw  Core_Code_Segement
     19     ;----------------------------------------------------------------
     20                             [bits 32]
     21 ;=========================================================================
     22 ;============================公用例程区===================================
     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        ;读一个扇区                                
     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        ;现在ax就是当前光标的位置
    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变成原来的两倍,实际位置是光标位置的两倍
    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    ;别总是忘了光标的位置!
    149             _set_cursor:        ;改变后的光标位置在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:                            ;简易内存分配策略
    172                                                 ;输入ecx:想要分配的总字节数
    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                                ;发现新分配的现地址比原来的还小,说明已经溢出
    185         jge _alloc
    186             mov ebx,mem_alloc_fail
    187             call Sys_Routine_Segement:put_string
    188             mov ecx,0                        ;分配为0说明已经分配失败
    189             jmp _exit1
    190         _alloc:
    191             
    192         mov ebx,eax
    193         and ebx,0xfffffffc
    194         add ebx,4                             ;强行向上取整
    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                ;因为我们还没学到多任务,先这样简单地清零
    213         
    214         sgdt [pgdt_base_tmp]
    215         sub word[pgdt_base_tmp],32                    ;应用程序的4个段全部减掉
    216         lgdt [pgdt_base_tmp]                    ;重新加载内核
    217         retf
    218     ;----------------------------------------------------------------    
    219     PrintDword:                                ;显示edx内容的一个调试函数
    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:                        ;构造描述符
    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,因为gdt段地址初始化是0xffff的
    283                                             ;要用到回绕特性
    284         add ebx,[pgdt_base_tmp+0x02]        ;得到pgdt的线性基地址
    285         
    286         mov [es:ebx],eax
    287         mov [es:ebx+0x04],edx                ;装载新的gdt符
    288                                             ;装载描述符要装载到实际位置上
    289         
    290         add word[pgdt_base_tmp],8            ;给gdt的段界限加上8(字节)
    291         
    292         lgdt [pgdt_base_tmp]                ;加载gdt到gdtr的位置和实际表的位置无关
    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(申请特权0级)
    300         
    301         pop es
    302         pop ds
    303         retf                            
    304 ;=========================================================================
    305 ;===========================内核数据区====================================
    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                    ;下次分配内存时的起始地址(直接暴力从0x00100000开始分配了)
    313         ram_recycled            dd  0                              ;这里储存程序实际用的大小            
    314         salt:
    315         salt_1:                    db    '@Printf'                    ;@Printf函数(公用例程)
    316         times 256-($-salt_1)    db    0
    317                                 dd    put_string
    318                                 dw    Sys_Routine_Segement    
    319                                 
    320         salt_2:                    db    '@ReadHarddisk'                ;@ReadHarddisk函数(公用例程)
    321         times 256-($-salt_2)    db    0
    322                                 dd    ReadHarddisk
    323                                 dw    Sys_Routine_Segement
    324                                 
    325         salt_3:                    db    '@PrintDwordAsHexString'    ;@PrintDwordAsHexString函数(公用例程)
    326         times 256-($-salt_3)    db    0
    327                                 dd    PrintDword
    328                                 dw    Sys_Routine_Segement    
    329                                 
    330         salt_4:                    db    '@TerminateProgram'            ;@TerminateProgram函数(内核例程)
    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        ;得到项目总数
    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                             ;内核用的缓冲区(2049个字节(2MB))
    357 
    358         esp_pointer             dd 0                              ;内核用来临时保存自己的栈指针
    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 ;===========================内核代码区====================================
    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                        ;切换到内核数据段
    376         
    377         mov ebx,core_buf                ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了)
    378         call Sys_Routine_Segement:ReadHarddisk
    379         
    380         mov eax,[core_buf]                ;读取用户程序长度
    381         
    382         mov ebx,eax                        ;给ebx一个副本
    383         and ebx,0xfffffe00                ;清空低9位(强制对齐512)
    384         add ebx,512                        ;加上512
    385         test eax,0x000001ff                
    386         cmovnz eax,ebx                    ;低9位不为0则使用向上取整的结果
    387         
    388         mov ecx,eax                        ;eax是整个程序的向上取整的大小
    389         call Sys_Routine_Segement:allocate_memory    ;先分配内存给整个程序,再分配内存给栈区
    390         mov ebx,ecx                                    ;这个才是真正的程要用到的线性基地址
    391         
    392         push ebx                        ;ebx就是用户程序加载到内存的地址
    393         xor edx,edx
    394         mov ecx,512                        ;千万不要改掉ebx
    395         div ecx
    396         mov ecx,eax
    397         
    398         mov eax,All_4GB_Segment            ;切换到4GB段区域(平坦模式)
    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         ;加载用户程序头部段(下面的所有段的地址都要转化为选择子)
    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         ;加载用户程序数据段
    428         mov eax,edi        
    429         add eax,[edi+0x1c]                ;别忘记重定位了
    430         mov ebx,[edi+0x20]                ;用户程序数据段的长度
    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            ;加上分配的内存
    444         
    445         mov ecx,eax                        ;这个时候eax是大小
    446         call Sys_Routine_Segement:allocate_memory        ;分到内存
    447         add eax,ecx                        ;eax线性基地址,ebx段界限
    448         mov ecx,0x00c09600                ;4KB粒度向下拓展数据段
    449         call Sys_Routine_Segement:Make_Descriptor
    450         call Sys_Routine_Segement:Set_New_GDT
    451         mov [edi+0x08],cx
    452         
    453         ;现在开始重定位API符号表
    454         ;---------------------------------------------------------------------
    455         mov eax,[edi+0x04]                ;先设es再ds,不要搞反了,现在es的0x04这个地方是头部的选择子
    456         mov es,eax
    457         mov eax,Core_Data_Segement
    458         mov ds,eax
    459         
    460         cld
    461         mov ecx,[es:0x24]                ;得到用户程序符号表的条数
    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个字节
    477                 repe cmpsd
    478                 jne _re_match            ;如果成功匹配,那么esi和edi刚好会在数据区之后的
    479                 
    480                 mov eax,[esi]            ;偏移地址
    481                 mov [es:edi-256],eax    ;把偏移地址填入用户程序的符号区
    482                 mov ax,[esi+0x04]        ;段的选择子
    483                 mov [es:edi-252],ax        ;把段的选择子填入用户程序的段选择区
    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                        ;重新设置数据段和栈段
    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   ;数据段位置#0x1c
    17         data_len         dd data_end             ;数据段长度#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         ;缓冲区
    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            ;数据文件存放地点
    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                      ;缓冲区偏移地址
    66         call far [fs:ReadHarddisk]          ;相当于调用函数
    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]       ;将控制权返回到系统 
    75       
    76 code_end:
    77 
    78 ;===============================================================================
    79 SECTION trail
    80 ;-------------------------------------------------------------------------------
    81 program_end:

    2. 课后习题

           其实这一章的课后习题很无聊的,就是把栈段那里改一下就好了,用户程序按照第八章那样自己填一个区域。

       

     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   ;数据段位置#0x1c
    17         data_len         dd data_end             ;数据段长度#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         ;缓冲区
    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            ;数据文件存放地点
    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                      ;缓冲区偏移地址
    66         call far [fs:ReadHarddisk]          ;相当于调用函数
    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]       ;将控制权返回到系统 
    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:

     

     

  • 相关阅读:
    关于gitlab怎样merge request的流程
    有访问权限的gitlab如何把上面的代码clone到本地
    macpro终端打开mysql
    Hbase实验:java创建和删除table
    齐次递推式拆数学式
    一些生成函数
    圆锥表面曲线方程
    扩展欧拉降幂
    scanf读入有空格字符串
    线性筛素数的一个用途
  • 原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/5226696.html
Copyright © 2011-2022 走看看