• ASM:《X86汇编语言-从实模式到保护模式》第16章:Intel处理器的分页机制和动态页面分配


      第16章讲的是分页机制和动态页面分配的问题,说实话这个一开始接触是会把人绕晕的,但是这个的确太重要了,有了分页机制内存管理就变得很简单,而且能直接实现平坦模式。

    ★PART1:Intel X86基础分页机制

    1. 页目录、页表和页

      首先先要明白分页是怎么来的,简单来讲,分页其实就是内存块的映射管理。在我们之前的章节中,我们都是使用的分段管理模式,处理器中负责分段的部件是段部件,段管理机制是Intel处理器最基本的处理机制,在任何时候都是无法关闭的。而当开启了分页管理之后,处理器会把4GB的内存分成长度相同的段,也就是说用长度固定的页来代替长度不一的段。页的分配由处理器固件来进行,可以实现非常高效的操作。页的最小单位是4KB,也就是说4GB的内存可以分成1048576个页(页的物理地址的低12位全是0)。

      在分页模式下,操作系统可以创建一个为所有任务共用的4GB虚拟内存空间。也可以为每一个任务创建一个独立的4GB虚拟内存空间,当一个程序加载的时候,操作系统既要在虚拟内存空间中分配空间,而且也要在物理内存中分配相应的也页面。另外,如果允许页共享,多个段或者多个程序可以用同一个页来存放各自的数据。4GB虚拟内存空间只是一个用来指示内存使用状况的一个机制,当操作系统加载一个程序并且创建为任务时,操作系统在虚拟内存空间中寻找空闲的段,并映射到空闲的页中取去。然后,到真正加载程序的时候,再把原本属于段的数据按页的尺寸拆分,分开写入相应的页中。页最大的好处就是方便操作系统进行内存管理,特别是虚拟内存管理,每个任务都可以有4GB虚拟内存,但是假如机器没有那么大的内存,操作系统也可以认为确实存在那么大的内存,当一个程序使用的内存超过了实际的物理内存,那么操作系统就会搜索那些暂时用不到的页,并且把他们转移到磁盘中,并且调入马上要使用的页。(当然这种操作非常地花费时间,这就是为什么小内存的电脑会有很严重的卡顿现象)。

      Intel处理器的最基础的分页管理机制就是二级管理机制。(当然现在更新的处理器支持更复杂的分页操作,但是教材也没有提及。)如果每个操使用直接分页管理(也就是把4GB的内存直接分成1048576个页),这需要1048576个表项,每个表项是4字节,所以映射表的总大小是4MB。事实上程序往往用不到4GB的内存,所以1级映射是一个很严重的浪费。但是如果采用层次化分页管理,那么就会产生巨大的内存节约。所谓Intel的分页管理,其实就是把页拆成3个部分(页目录,页表和页)。

      页目录(Page Directory Table,PDT)的物理地址由PDBR(Page Directory Base Register,也就是CR3寄存器)指定,每个任务都可以有自己的页目录。每个任务的TSS段就有自己的CR3的物理地址。每次进行任务切换的时候,CR3的内容都会被替换改为新任务的CR3域中的物理地址。页目录也是一个的页。但是他里面存放的是页表的地址,所以页目录可以指向1024个页表。

      页表也可以指向1024个页,每个项也是4个字节,同样页表的大小也是页的大小。

      页目录,页表和页的关系:(图片出处看水印)

      

      处理器有页部件,专门负责线性地址到物理地址的转换工作。它首先将段部件送来的32位线性地址截成3段,分别是高10位(页目录的索引),中间的10位(页表的索引)和低12位(页内偏移)。操作系统要负责填写页目录和页表地址,然后程序的内存访问就可以像上图那样进行转换了。

    2. 页目录、页表和CR3的填写

      

      P(Present)是存在位,为“1”时,表示页表或者页位于内存中,否则,表示页表或者页不在内存中,必须先予以创建,或者从磁盘调入内存后方可使用。

      RW(Read/Write)是读/写位,为“0”时表示这样的页只能读取,为“1”时,可读可写。

      US(User/Supervisor)是用户/管理位。为“1”时,允许所有特权级别的程序访问;为“0”时,只允许特权级为0,1,2的程序访问,特权级为3的程序不能访问。

      PWT(Page-Level Write-Through)是页级通写位,和高速缓存有关,“通写”是处理器高速缓存的一种工作方式,这意味用来间接决定是否采用此种方式来改善页面的访问效率。

      A(Accessed)是访问位。该位由处理器固件来设置,用来指示此表项所指向的页是否被访问过。(这个坑爹的属性将会在练习题中体现)。

      D(Dirty)是脏位。该位由处理器固件来设置,用来指示此表项所指向的页是否被写入数据。(和A位一样,练习题会有体现)。

      PAT(Page Attribute Table)页属性表支持位。此位涉及更为复杂的分页系统。和页高速缓存有关。

      G(Global)是全局位,用来只是该表项指向的页是否为全局属性。如果页是全局属性的,那么,他将一直在高速缓存中一直保存(地址转换速度会加快)。因为页高速缓存的缓存容量有限,只能存放频繁使用的那些表项。而且,当因为任务切换等原因改变CR3寄存器的内容时,整个页高速缓存的内容都会被刷新。

      AVL位被处理器忽略,软件可以使用。

      进入分页模式之后,所有东西的地址都变成了虚拟线性地址了,包括GDT,LDT和TSS的地址等等。需要注意的是,页目录和页表在内存中的位置必须是在有效的可用内存范围(比如2GB内存只能在2GB内存中设置页目录和页表)。注意页目录必须进行初始化,主要是对P位置零,如果P位等于0,而处理器又执行了一个对这个页表的访问,那么就会引发异常中断。

           清空页表以后就是要对页目录进行初始化了,在教材的“系统”中,把页目录设置在0x00020000这个物理地址中。然后在页目录中的最后一项,指向页目录自己。然后只进行最底下1MB内存的页面设置(所以只需要一个页表),然后就可以进行页表的初始化了,初始化过后。就可以直接进行CR3的设置,CR3的设置如下图:

      

      其实CR3的设置和页表和页的填写差不多,只是除了PCD位和PWT位外,其他都不需要填写。

           最后一步就是打开分页了,这个开关也是在CR0那里,称为PG位,在CR0的最高位31位上,注意这个功能只有在保护模式下才能开启(也就是CR0的PE位(0位)要是1),当PG位为1的时候,段部件传来的内存地址必须要经过页部件的转换才能得到真实的内存地址。

           有关CR0-3这4个寄存器的控制可以看:http://www.cnblogs.com/Philip-Tell-Truth/articles/5341686.html

      

      

      以上代码就就是开启页功能的一个演示。

    3. 任务全局空间和局部空间的页面演示,页面查找的例子

           在15章我们说过,任务的4GB地址空间包括两个部分:局部空间和全局空间,全局空间是所有任务共用的,内核就是所有任务共用的,它属于每个任务的全局空间。在教材的系统中,规定4GB的虚拟高2GB空间就是全局空间,地址范围是0x80000000-0xFFFFFFFF,局部空间使用低2GB空间,地址范围是0x00000000-0x7FFFFFFF,在任何时候,如果段部件发出的线性地址高于等于0x80000000,指向和访问的就是全局地址空间,或者说是内核。另外在上面我说过,我们把页目录的最后一项指向自己页目录的页,把页目录看成页表,再把页目录看成页表,刚好这个页表的最后一项也是指向页目录,那样我们就可以实现对页目录的内容进行访问,否则我们将无法访问页目录,因为处理器不允许访问一个没有登记的页(访问了会引发异常中断)。

           但是为了保证内核的全局部分可以被其他程序使用,要创建内核的映射,内核应该具有两个部分,一个是和他的物理地址一样的对应(低地址)部分,另一个是映射到高地址的对应部分。这样说可能会比较抽象,我们直接看代码就好了。然后接下来就是把GDT的内容全部重定位了(GDT页必须使用虚拟线性地址,已经在前面把内核映射到高地址空间了),当然我们这里的全局空间映射位置取的比较好,是0x80000000,所以只要往GDT的最高4位or一个8就可以了。

       

      注意 mov dword[es:ebx+esi],PDT_Mem_Address,add dword[es:ebx+esi],0x00001003这两句话,ebx的内容是0XFFFFF000,ESI的内容是0x00000200,因此段部件发出的线性地址是0XFFFFF200,现在举这个访问页目录的例子,以更加清楚了解如何访问页:

       

      

      所以现在位于物理地址0x00021000这个地方的页被页目录中两个项所指向,但是这两个项所映射的物理地址时不一样的!!!!,页目录的第0项对应的映射是0x00000000-0x000FFFFF,0x800项对应的是0x80000000-0x800FFFFF。这也是一个分级管理有效地缩减表的占用的一个证据:因为全局空间总是映射到高区间,所以如果采用单映射的话,必须先准备2MB的表项先把局部空间描述完了才能到全局空间。

      注意因为段部件的内容不会因为你做好了映射而自动把自己的内容改变,所以需要显式切换,那就是上面代码的后面的部分。

    ★PART2:保护模式页管理模式下的内核任务的创建

    1. 内核的虚拟内存的分配

      说实话教材说了那么多,其实就是最重要的是讲明白一个东西就行了,那就是页的分配。把这个搞明白了其他东西都是一个套路。

      现代操作系统可以跟踪所有页的分配状况。内存空间来自于插在主板上的内存条,按照新的工业标准,每个内存条上焊有一个很小的只读存储器,用于标明该内存条的容量和工作参数,作为一个PCI(E)设备,软件可以读取它,以获得计算机上的物理内存容量。然后简历上述的页分配表。但是由固件创建的表是每个表项占1个字节的,如果有4GB内存,则最多分220个页,要占用1MB的内存。但是如果以比特来指示页的使用状况,那么最多使用1048576个比特(128KB),将会产生巨大的内存节约。我们的系统为了简单,假定我们的系统只有2MB的内存,2MB的内存,可以分512个页,需要512个比特,我们可以在内核区定义比特串:

       

      我们可以看到底下的1MB已经被内核和ROM-BIOS分配的差不多了。但是页的分配可以不连续的,接下来我们就可以在代码中看到这个问题。

     

     

    注意代码中我们使用了bts指令,这个指令用于测试串中的某个比特位。用该比特的值设置EFLAGS寄存器的CF标志位,然后将这个标志置“1”,他的最基本两种形式为:

                         bts r/m16,r16

                         bts r/m32,r32

      目的操作数可以是16/32位的通用寄存器,或者指向一个包含了16/32位实际操作数的内存党员,用于指定位串;源操作数可以是16/32位的通用寄存器,用于指定待测试的比特在位串的索引 (位置)。其他类似的指令还有:btr,btc和bt,他们的区别如下:

      例程首先做的事情就是在页目录看一下相应的页表是否已经在页目录中登记了,如果没有登记,则分配一个新的页作为页表然后写入页目录相应位置,否则就直接使用这个页表(当然这个管理程序非常粗糙,会有非常严重的问题,但是现在我们要把事情简单化)。

    2. 任务程序的加载

      其实也是一堆套路,只不过我们需要注意的是,因为我们现在用的是页管理模式,所以要使用平坦模式。平坦模式是现代操作系统流行的管理模式,抛弃段管理模式冗杂的段的管理,可以大大简化代码的编写难度(注意的是页也有一点特权控制)。

      教材上加载程序的思路其实和前面几章是一样的,但是使用的是平坦模式来加载,并且对TCB进行了一些改变,而且使用了向上拓展的栈段(其实向上拓展的栈段只是段界限的检查会不一样,那是处理器的事情,push和pop指令的操作是一样的)。TCB变成了下图这个样子:

      最后要说明的是,分页机制下,和分段机制是一样的,内存也是先登记后使用的。程序在编写和编译之后,都是连续的,在加载后不能保证这一点。页的分配是随机的,尽管页不是连续的,但是线性地址是连续的就足够了。处理器访问数据,取指令,用的是线性地址。教材把所有的用户程序都从0x00000000开始加载,其实是不合理的,多段模型之下段内元素的偏移量都是相对于段而言的,在程序加载之后,段的描述符的基地址,就是段实际加载的位置。也就是这样,多段模型下,不管段加载到哪里,都不会影响段内元素的访问,这就是多段模型下程序可以重定位和浮动的根本原因。

      而在平坦模式下,程序的重定位和浮动实现比较复杂,在现代流行的操作系统中,编写的程序必须符合一定的规范才能重定位,比如很多系统要求用户提供一个标准的重定位表,列出所有需要动态加载的元素,程序加载后,操作系统会找到这个表,用实际的加载地址修正每一个表项。(非常复杂的一个过程)。

    ★PART3:本章的课后练习题

    1. 显示当前任务的当前任务的虚拟地址的前55个双字,前50个页面的物理地址

           显示虚拟地址前55个双字书上是有例程的,就是物理地址这个比较麻烦一点,其实也不麻烦,主要是要把页目录指向也目录自己和指向内核的那两个页表项修改为特权级3的程序也能访问。说下几个坑:

           1. 之前几章的PrintDword过程都是有问题的,因为我忽略了我的put_char过程和书上的不一样,要把put_char压栈的那几个操作改成pushad,出栈改为popad

           2. 页目录/页表内登记的物理地址的低12位都是有含义的!页表/页的物理地址的31-12位才会出现在项中,低12位是属性!特别是A和D位,处理器会自动将他们设置。显示物理内存的时候低12位都是0(and一下,页都是4KB对齐的)。

       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_AddressA    equ 50            ;用户程序所在逻辑扇区
      11         User_Program_AddressB    equ 80            ;用户程序所在逻辑扇区
      12         Switch_Stack_Size        equ 4096        ;切换栈段的大小
      13         PDT_Mem_Address            equ 0x00020000    ;PDT在内存加载的地址
      14         Global_Page_Directory    equ 0x80000000     ;给全局空间的映射地址
      15 ;=============================内核程序头部===============================
      16 SECTION header vstart=0
      17         Program_Length             dd    Program_end                    ;内核总长度
      18         Sys_Routine_Seg         dd  section.Sys_Routine.start    ;公用例程段线性地址
      19         Core_Data_Seg             dd  section.Core_Data.start        ;内核数据区线性地址
      20         Core_Code_Seg             dd  section.Core_Code.start        ;内核代码区线性地址
      21         Code_Entry                dd    start                        ;注意偏移地址一定是32位的
      22                                 dw  Core_Code_Segement
      23     ;----------------------------------------------------------------
      24                             [bits 32]
      25 ;=========================================================================
      26 ;============================公用例程区===================================
      27 ;=========================================================================
      28 SECTION Sys_Routine align=16 vstart=0
      29     ReadHarddisk:                            ;push1:28位磁盘号(esi)
      30                                             ;push2:应用程序数据段选择子(ax->ds)
      31                                             ;push3: 偏移地址(ebx)
      32                                             ;push4: 应用程序代码段选择子(dx)
      33         pushad
      34         push ds
      35         push es
      36         
      37         mov ebp,esp
      38         
      39         mov esi,[ebp+15*4]
      40         movzx eax,word[ebp+14*4]
      41         mov ebx,[ebp+13*4]
      42         movzx edx,word[ebp+12*4]
      43         
      44         arpl ax,dx
      45         mov ds,ax
      46         
      47         mov dx,0x1f2
      48         mov al,0x01        ;读一个扇区                                
      49         out dx,al
      50         
      51         inc edx            ;0-7位
      52         mov eax,esi
      53         out dx,al
      54         
      55         inc edx            ;8-15位
      56         mov al,ah
      57         out dx,al
      58         
      59         inc edx            ;16-23位
      60         shr eax,16
      61         out dx,al
      62         
      63         inc edx            ;24-28位,主硬盘,LBA模式
      64         mov al,ah
      65         and al,0x0f
      66         or al,0xe0
      67         out dx,al
      68         
      69         inc edx
      70         mov al,0x20
      71         out dx,al
      72         
      73         _wait:
      74             in al,dx
      75             and al,0x88
      76             cmp al,0x08
      77             jne _wait
      78         
      79         mov dx,0x1f0
      80         mov ecx,256
      81         _read:
      82             in ax,dx
      83             mov [ebx],ax
      84             add ebx,2
      85         loop _read
      86         
      87         pop es
      88         pop ds
      89         popad
      90         retf 16        ;4个数据
      91     ;----------------------------------------------------------------
      92     put_string:                                                    ;ebx:偏移地址
      93         pushad
      94         push ds
      95         push es
      96         
      97         _print:
      98             mov cl,[ebx]
      99             cmp cl,0
     100             je _exit
     101             call put_char
     102             inc ebx
     103             jmp _print
     104         _exit:
     105             pop es
     106             pop ds
     107             popad
     108             retf            ;段间返回
     109         ;--------------------------------------------------------------    
     110         put_char:            ;cl就是要显示的字符
     111             pushad
     112             push es
     113             push ds
     114             
     115             mov dx,0x3d4
     116             mov al,0x0e        ;高8位
     117             out dx,al
     118             mov dx,0x3d5
     119             in al,dx
     120             mov ah,al        ;先把高8位存起来
     121             mov dx,0x3d4
     122             mov al,0x0f        ;低8位
     123             out dx,al
     124             mov dx,0x3d5
     125             in al,dx        ;现在ax就是当前光标的位置
     126             
     127             _judge:
     128                 cmp cl,0x0a
     129                 je _set_0x0a
     130                 cmp cl,0x0d
     131                 je _set_0x0d
     132             _print_visible:
     133                 mov bx,ax
     134                 mov eax,Print_Segement
     135                 mov es,eax
     136                 shl bx,1     ;注意这里一定要把ebx变成原来的两倍,实际位置是光标位置的两倍
     137                 mov [es:bx],cl            ;注意这里是屏幕!
     138                 mov byte[es:bx+1],0x07        
     139                 add bx,2
     140                 shr bx,1
     141                 jmp _roll_screen
     142             _set_0x0d:        ;回车
     143                 mov bl,80
     144                 div bl
     145                 mul bl
     146                 mov bx,ax
     147                 jmp _set_cursor
     148             _set_0x0a:        ;换行
     149                 mov bx,ax
     150                 add bx,80
     151                 jmp _roll_screen
     152             _roll_screen:
     153                 cmp bx,2000
     154                 jl _set_cursor
     155                 mov eax,Print_Segement
     156                 mov ds,eax
     157                 mov es,eax
     158                 
     159                 cld
     160                 mov edi,0x00
     161                 mov esi,0xa0
     162                 mov ecx,1920
     163                 rep movsw
     164             _cls:
     165                 mov bx,3840
     166                 mov ecx,80
     167                 _print_blank:
     168                     mov word[es:bx],0x0720
     169                     add bx,2
     170                     loop _print_blank    
     171                 mov bx,1920    ;别总是忘了光标的位置!
     172             _set_cursor:        ;改变后的光标位置在bx上
     173             mov dx,0x3d4
     174             mov al,0x0f        ;低8位
     175             out dx,al
     176             
     177             mov al,bl
     178             mov dx,0x3d5
     179             out dx,al
     180             
     181             mov dx,0x3d4
     182             mov al,0x0e     ;高8位
     183             out dx,al
     184             
     185             mov al,bh
     186             mov dx,0x3d5
     187             out dx,al
     188             
     189             pop ds
     190             pop es
     191             popad
     192             ret
     193     ;----------------------------------------------------------------        
     194     Make_Seg_Descriptor:                    ;构造段描述符
     195                                             ;输入:
     196                                             ;eax:线性基地址
     197                                             ;ebx:段界限
     198                                             ;ecx:属性
     199                                             ;输出:
     200                                             ;eax:段描述符低32位
     201                                             ;edx:段描述符高32位
     202         mov edx,eax
     203         and edx,0xffff0000
     204         rol edx,8
     205         bswap edx
     206         or edx,ecx
     207         
     208         shl eax,16
     209         or ax,bx
     210         and ebx,0x000f0000
     211         or edx,ebx
     212         retf                
     213     ;----------------------------------------------------------------        
     214     Make_Gate_Descriptor:                    ;构造门描述符
     215                                             ;输入:
     216                                             ;eax:段内偏移地址
     217                                             ;bx: 段的选择子
     218                                             ;cx: 段的属性
     219                                             ;输出:
     220                                             ;eax:门描述符低32位
     221                                             ;edx:门描述符高32位
     222         push ebx
     223         push ecx
     224         
     225         mov edx,eax
     226         and edx,0xffff0000                    ;要高16位
     227         or dx,cx
     228         
     229         shl ebx,16
     230         and eax,0x0000ffff
     231         or eax,ebx
     232         
     233         pop ecx
     234         pop ebx
     235         
     236         retf                
     237     ;----------------------------------------------------------------
     238     Set_New_GDT:                            ;装载新的全局描述符
     239                                             ;输入:edx:eax描述符
     240                                             ;输出:cx选择子
     241         push ds
     242         push es
     243         
     244         mov ebx,Core_Data_Segement
     245         mov ds,ebx
     246         
     247         mov ebx,All_4GB_Segment
     248         mov es,ebx
     249         
     250         sgdt [pgdt_base_tmp]
     251         
     252         movzx ebx,word[pgdt_base_tmp]
     253         inc bx                                ;注意这里要一定是inc bx而不是inc ebx,因为gdt段界限初始化是0xffff的
     254                                             ;要用到回绕特性
     255         add ebx,[pgdt_base_tmp+0x02]        ;得到pgdt的线性基地址
     256         
     257         mov [es:ebx],eax
     258         mov [es:ebx+0x04],edx                ;装载新的gdt符
     259                                             ;装载描述符要装载到实际位置上
     260         
     261         add word[pgdt_base_tmp],8            ;给gdt的段界限加上8(字节)
     262         
     263         lgdt [pgdt_base_tmp]                ;加载gdt到gdtr的位置和实际表的位置无关
     264         
     265         mov ax,[pgdt_base_tmp]                ;得到段界限
     266         xor dx,dx
     267         mov bx,8                            ;得到gdt大小
     268         div bx
     269         mov cx,ax
     270         shl cx,3                            ;得到选择子,ti=0(全局描述符),rpl=0(申请特权0级)
     271         
     272         pop es
     273         pop ds
     274         retf
     275     ;----------------------------------------------------------------
     276     Set_New_LDT_To_TCB:                        ;装载新的局部描述符
     277                                             ;输入:edx:eax描述符
     278                                             ;     : ebx:TCB线性基地址
     279                                             ;输出:cx选择子
     280         
     281         push edi
     282         push eax
     283         push ebx
     284         push edx
     285         push ds
     286         
     287         mov ecx,All_4GB_Segment
     288         mov ds,ecx
     289         
     290         mov edi,[ebx+0x0c]                    ;LDT的线性基地址
     291         movzx ecx,word[ebx+0x0a]
     292         inc cx                                ;得到实际的LDT的大小(界限还要-1)
     293         
     294         mov [edi+ecx+0x00],eax
     295         mov [edi+ecx+0x04],edx
     296         
     297         add cx,8
     298         dec cx
     299         
     300         mov [ebx+0x0a],cx
     301         
     302         mov ax,cx
     303         xor dx,dx
     304         mov cx,8
     305         div cx
     306         
     307         shl ax,3
     308         mov cx,ax
     309         or cx,0x0004                        ;LDT,第三位TI位一定是1
     310         
     311         pop ds
     312         pop edx
     313         pop ebx
     314         pop eax
     315         pop edi
     316         retf
     317     ;----------------------------------------------------------------    
     318     PrintDword:                                ;显示edx内容的一个调试函数
     319         pushad
     320         push ds
     321         
     322         mov eax,Core_Data_Segement
     323         mov ds,eax
     324         
     325         mov ebx,bin_hex
     326         mov ecx,8
     327         
     328         _query:
     329             rol edx,4
     330             mov eax,edx
     331             and eax,0x0000000f
     332             xlat
     333             
     334             push ecx
     335             mov cl,al
     336             call put_char
     337             pop ecx
     338             
     339         loop _query
     340             
     341         pop ds
     342         popad
     343         
     344         retf
     345     ;----------------------------------------------------------------
     346     allocate_4KB_page:                            ;输入:无
     347                                                 ;输出eax:页的物理地址
     348                                                 ;注意这个是近调用
     349         push ebx
     350         push ecx
     351         push edx
     352         push ds
     353         
     354         mov eax,Core_Data_Segement
     355         mov ds,eax
     356         
     357         xor eax,eax
     358         
     359         _search_pages:
     360             bts [page_bit_map],eax
     361             jnc _found_not_uesd
     362             inc eax
     363             cmp eax,page_map_len*8
     364         jl _search_pages
     365         
     366         mov ebx,No_More_Page
     367         call Sys_Routine_Segement:put_string
     368         hlt                                     ;无可用页,直接停机
     369         
     370         _found_not_uesd:
     371         shl eax,12                                ;eax相当于是选择子,乘以一个4KB得到物理地址
     372         
     373         pop ds
     374         pop edx
     375         pop ecx
     376         pop ebx
     377         ret
     378     ;----------------------------------------------------------------
     379     alloc_inst_a_page:                            ;分配一个页,并安装在当前活动的层级分页结构中
     380                                                 ;输入:EBX=页的线性地址
     381                                                 ;输出:无
     382         push eax
     383         push ebx
     384         push edi
     385         push esi
     386         push ds
     387         
     388         mov eax,All_4GB_Segment                    ;转平坦模式
     389         mov ds,eax
     390         
     391         _test_P:                                ;在页目录中看是否存在这个页表
     392             mov esi,ebx
     393             and esi,0xffc00000
     394             shr esi,20                                         
     395             or esi,0xfffff000                    ;指向页目录本身
     396             test dword[esi],0x00000001
     397             jnz _get_page_and_create_new_page
     398         _create_new_page_directory:
     399             call allocate_4KB_page
     400             or eax,0x00000007                    ;存在于主存,可读可写,允许特权级3程序访问
     401             mov [esi],eax        
     402         _get_page_and_create_new_page:    
     403             mov esi,ebx
     404             shr esi,10                            ;页表在页目录的偏移项
     405             and esi,0x003ff000                    ;得到页表的偏移地址
     406             or esi,0xffc00000                    ;指向页目录
     407             
     408             and ebx,0x003ff000
     409             shr ebx,10                            ;中间10位是页目录-页表-表内偏移量(注意这里的层次理解)
     410             or esi,ebx                            ;esi就是页的对应线性地址
     411             call allocate_4KB_page
     412             or eax,0x00000007                    ;存在于主存,可读可写,允许特权级3程序访问
     413             mov [esi],eax
     414             
     415         pop ds
     416         pop esi
     417         pop edi
     418         pop ebx
     419         pop eax
     420         retf
     421     ;----------------------------------------------------------------
     422     Copy_Page:                                    ;把在创建的包含全局和私有部分的页表复制一份给用户程序用
     423                                                 ;输入:无
     424                                                 ;输出eax:页的物理地址
     425         push ds
     426         push es
     427         push edi
     428         push esi
     429         push ebx
     430         push ecx
     431         push edx
     432         
     433         mov eax,Core_Data_Segement
     434         mov ds,eax
     435         mov edx,[task_pos]
     436         sub edx,4
     437         add edx,[page_header]
     438         mov edi,[page_soft_header]
     439         sub edi,0x1000
     440         mov esi,[page_header]                        ;指向全局页目录
     441         mov [es:0x16],edi
     442         
     443         mov eax,All_4GB_Segment
     444         mov ds,eax
     445         mov es,eax
     446         
     447         call allocate_4KB_page
     448         mov ebx,eax
     449         or ebx,0x00000007
     450         mov [edx],ebx
     451                             
     452         mov ecx,1024
     453         cld
     454         repe movsd
     455         
     456         pop edx
     457         pop ecx
     458         pop ebx
     459         pop esi
     460         pop edi
     461         pop es
     462         pop ds
     463         
     464         retf
     465     ;----------------------------------------------------------------
     466 ;=========================================================================
     467 ;===========================内核数据区====================================
     468 ;=========================================================================
     469 SECTION Core_Data align=16 vstart=0
     470 ;-------------------------------------------------------------------------------
     471         pgdt_base_tmp:          dw  0                             
     472                                 dd  0
     473 
     474         salt:
     475         salt_1:                    db    '@Printf'                    ;@Printf函数(公用例程)
     476         times 256-($-salt_1)    db    0
     477                                 dd    put_string
     478                                 dw    Sys_Routine_Segement
     479                                 dw  0                            ;参数个数
     480                                 
     481         salt_2:                    db    '@ReadHarddisk'                ;@ReadHarddisk函数(公用例程)
     482         times 256-($-salt_2)    db    0
     483                                 dd    ReadHarddisk
     484                                 dw    Sys_Routine_Segement
     485                                 dw  4                            ;参数个数
     486                                 
     487         salt_3:                    db    '@PrintDwordAsHexString'    ;@PrintDwordAsHexString函数(公用例程)
     488         times 256-($-salt_3)    db    0
     489                                 dd    PrintDword
     490                                 dw    Sys_Routine_Segement
     491                                 dw  0                            ;参数个数
     492                                 
     493         salt_length:            equ    $-salt_3
     494         salt_items_sum            equ    ($-salt)/salt_length        ;得到项目总数
     495         
     496         salt_tp:                dw    0                            ;任务门,专门拿来给程序切换到全局空间的
     497         
     498         message_1                db  '   If you seen this message,that means we '
     499                                 db  'are now in protect mode,and the system '
     500                                 db  'core is loaded,and the video display '
     501                                 db  'routine works perfectly.',0x0d,0x0a,0
     502 
     503         message_2                db  '   Loading user program...',0
     504 
     505         do_status                db  'Done.',0x0d,0x0a,0
     506 
     507         message_3                db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
     508                                 db  '   User program terminated,control returned.'
     509                                 db  0x0d,0x0a,0x0d,0x0a,0
     510         message_4                db  '   We have been backed to kernel.',0x0d,0x0a,0
     511         message_5                db  '   The GDT and memory have benn recycled.',0
     512         message_6                db  '   From the system wide gate:',0x0d,0x0a,0
     513         message_7                db  '   Setting the gate discriptor...',0
     514         message_In_Gate            db  '   Hi!My name is Philip:',0x0d,0x0a,0
     515         message_page            db  '  Paging is enabled.System core is mapped to'
     516                                 db  ' address 0x80000000.',0x0d,0x0a,0
     517         core_stop                db  0x0d,0x0a,'  Processor HALT.',0
     518         No_More_Page            db  '********No more pages********',0
     519         task_switch                db  0x0d,0x0a,'  Task switching...@_@',0x0d,0x0a,0
     520 
     521         bin_hex                  db '0123456789ABCDEF'
     522                                                                 ;put_hex_dword子过程用的查找表
     523         core_buf        times 2048 db  0                             ;内核用的缓冲区(2049个字节(2MB))
     524 
     525         esp_pointer             dd  0                              ;内核用来临时保存自己的栈指针
     526 
     527         cpu_brnd0                db  0x0d,0x0a,'  ',0
     528         cpu_brand         times 52 db  0
     529         cpu_brnd1                db  0x0d,0x0a,0x0d,0x0a,0
     530         core_ss                    dw  0
     531         core_sp                    dd  0
     532         ;程序管理器的任务信息 
     533         prgman_tss               dd  0                             ;程序管理器的TSS基地址
     534                                 dw  0                             ;程序管理器的TSS描述符选择子 
     535         ;假设只有2MB内存可以用的意思,正确的做法应该先读PCI(E),然后再分配!
     536         page_bit_map             db  0xff,0xff,0xff,0xff,0xff,0x55,0x55,0xff
     537                                 db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
     538                                 db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
     539                                 db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
     540                                 db  0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
     541                                 db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
     542                                 db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
     543                                 db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
     544         page_map_len             equ $-page_bit_map
     545         core_next_laddr          dd  0x80100000                    ;内核空间中下一个可分配的线性地址
     546         task_pos                dd  0x00000ffc                     ;任务程序的页表在全局页目录的偏移
     547         page_header                dd  0xfffff000                    ;全局页目录
     548         page_soft_header        dd  0xfffff000                     ;加载页目录地址
     549          
     550         tcb_chain                dd  0                            ;任务控制块链头指针
     551 ;=========================================================================
     552 ;===========================内核代码区====================================
     553 ;=========================================================================
     554 SECTION Core_Code align=16 vstart=0        
     555     ;---------------------------------------------------------------------
     556     append_to_tcb:                        ;写入新的TCB链
     557                                         ;输入:ecx新的TCB线性基地址
     558         pushad
     559         
     560         push ds
     561         push es
     562         
     563         mov eax,All_4GB_Segment
     564         mov es,eax
     565         
     566         mov eax,Core_Data_Segement
     567         mov ds,eax
     568         
     569         mov dword[es:ecx+0x00],0
     570         mov eax,[tcb_chain]
     571         cmp eax,0x00
     572         je _notcb
     573         
     574         _search_tcb:
     575             mov edx,[tcb_chain+0x00]
     576             mov eax,[es:edx]
     577             cmp eax,0x00
     578         jne _search_tcb
     579         
     580         mov [es:edx+0x00],ecx
     581         jmp _out_tcb_search
     582         
     583         _notcb:
     584         mov [tcb_chain],ecx
     585         
     586         _out_tcb_search:
     587         pop es
     588         pop ds
     589         
     590         popad
     591         ret
     592     ;---------------------------------------------------------------------    
     593     load_program:                        ;输入push1:逻辑扇区号
     594                                         ;     push2:    线性基地址
     595         pushad
     596         push ds
     597         push es
     598         
     599         mov ebp,esp                        ;别忘了把参数传给ebp
     600         
     601         mov eax,Core_Data_Segement
     602         mov ds,eax                        ;切换到内核数据段
     603         
     604         mov eax,All_4GB_Segment
     605         mov es,eax
     606         
     607         mov ebx,0xfffff000
     608         xor esi,esi
     609         _flush_private:
     610             mov dword[es:ebx+esi*4],0x00000000
     611             inc esi
     612             cmp esi,512
     613         jl _flush_private
     614         
     615         mov edi,[ebp+11*4]                ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push
     616         mov esi,[ebp+12*4]                ;esi必须是逻辑扇区号
     617         mov ebx,core_buf                ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了)
     618         
     619         push esi
     620         push ds
     621         push ebx
     622         push cs
     623         call Sys_Routine_Segement:ReadHarddisk
     624         
     625         mov eax,[core_buf]                ;读取用户程序长度
     626         mov ebx,eax                        
     627         and ebx,0xfffff000                ;清空低12位(强制对齐4096:4KB)
     628         add ebx,4096                        
     629         test eax,0x00000fff                
     630         cmovnz eax,ebx                    ;低12位不为0则使用向上取整的结果
     631         
     632         mov ecx,eax
     633         shr ecx,12                        ;看占了几页
     634         mov eax,All_4GB_Segment            ;切换到4GB段区域(平坦模式)
     635         mov ds,eax
     636         mov edi,[ebp+11*4]                ;获取tcb的线性基地址
     637         mov esi,[ebp+12*4]                ;esi必须是逻辑扇区号
     638         
     639         _loop_read_@1:
     640             mov ebx,[es:edi+0x06]        ;从TCB取得下一个可用的虚拟线性地址
     641             add dword[es:edi+0x06],0x1000
     642             call Sys_Routine_Segement:alloc_inst_a_page
     643             
     644             push ecx
     645             mov ecx,8                    ;512*8==4096
     646             _loop_read_@2:
     647                 push esi
     648                 push ds
     649                 push ebx
     650                 push cs
     651                 call Sys_Routine_Segement:ReadHarddisk    ;esi还是User_Program_Address
     652                 
     653                 mov eax,[es:0xffc00000]
     654                 mov eax,[es:0xffc00004]
     655                 mov eax,[es:0xffc00008]
     656                 
     657                 inc esi
     658                 add ebx,512
     659             loop _loop_read_@2
     660             pop ecx
     661         loop _loop_read_@1
     662         
     663         mov eax,Core_Data_Segement        ;把数据段切回来
     664         mov ds,eax
     665         mov esi,edi                        ;esi: TCB的线性基地址
     666         
     667         mov ebx,[core_next_laddr]        ;TSS必须在全局空间中进行
     668         call Sys_Routine_Segement:alloc_inst_a_page
     669         add dword[core_next_laddr],4096
     670         
     671         mov [es:esi+0x14],ebx            ;填写TSS的线性基地址
     672         mov word[es:esi+0x12],103        ;无I/O映射
     673         
     674         mov ebx,[es:esi+0x06]            ;从用户私有空间建立LDT
     675         add dword[es:esi+0x06],0x1000    
     676         call Sys_Routine_Segement:alloc_inst_a_page
     677         mov [es:esi+0x0c],ebx            ;填写LDT的线地址
     678         
     679         mov edi,[es:esi+0x14]            ;edi就是TSS的线性地址
     680         ;接下来就是一堆套路了,这里和15章14章的程序不一样,这里直接填TSS,比较直观
     681         ;不用给用户程序头回填段的选择子了,现在是平坦模式的演示
     682         ;代码段
     683         mov eax,0x00000000
     684         mov ebx,0x000fffff
     685         mov ecx,0x00c0f800
     686         call Sys_Routine_Segement:Make_Seg_Descriptor
     687         mov ebx,esi
     688         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     689         or cx,0x0003                    ;特权级为3
     690         mov [es:edi+76],cx                ;CS域
     691         
     692         ;数据段
     693         mov eax,0x00000000
     694         mov ebx,0x000fffff
     695         mov ecx,0x00c0f200
     696         call Sys_Routine_Segement:Make_Seg_Descriptor
     697         mov ebx,esi
     698         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     699         or cx,0x0003                    ;特权级为3
     700         mov [es:edi+72],cx                ;ES域,已经映射到全局空间了
     701         mov [es:edi+84],cx                ;DS域,已经映射到全局空间了
     702         mov [es:edi+88],cx                ;FS域,已经映射到全局空间了
     703         mov [es:edi+92],cx                ;GS域,已经映射到全局空间了
     704         
     705         ;创建一系列栈
     706         ;创建自身特权级为3的栈
     707         mov ebx,[es:esi+0x06]            
     708         add dword[es:esi+0x06],0x1000    
     709         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了
     710         mov [es:edi+80],cx                                ;cx是数据段的选择子
     711         mov edx,[es:esi+0x06]                            ;向上拓展的ESP的初始值
     712         mov [es:edi+56],edx            
     713         
     714         ;创建特权级0的栈
     715         mov ebx,[es:esi+0x06]            
     716         add dword[es:esi+0x06],0x1000    
     717         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了
     718         
     719         mov eax,0x00000000
     720         mov ebx,0x000fffff
     721         mov ecx,0x00c09200                                 ;4KB粒度的堆栈段描述符,特权级0
     722         call Sys_Routine_Segement:Make_Seg_Descriptor
     723         mov ebx,esi
     724         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     725         or cx,0x0000                                    ;选择子特权级为0
     726         
     727         mov [es:edi+8],cx                                ;cx是数据段的选择子
     728         mov edx,[es:esi+0x06]                            ;向上拓展的ESP0的初始值
     729         mov [es:edi+4],edx            
     730         
     731         ;创建特权级1的栈
     732         mov ebx,[es:esi+0x06]            
     733         add dword[es:esi+0x06],0x1000    
     734         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了
     735         
     736         mov eax,0x00000000
     737         mov ebx,0x000fffff
     738         mov ecx,0x00c0b200                                 ;4KB粒度的堆栈段描述符,特权级1
     739         call Sys_Routine_Segement:Make_Seg_Descriptor
     740         mov ebx,esi
     741         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     742         or cx,0x0001                                    ;选择子特权级为1
     743         
     744         mov [es:edi+16],cx                                ;cx是数据段的选择子
     745         mov edx,[es:esi+0x06]                            ;向上拓展的ESP1的初始值
     746         mov [es:edi+12],edx        
     747         
     748         ;创建特权级2的栈
     749         mov ebx,[es:esi+0x06]            
     750         add dword[es:esi+0x06],0x1000    
     751         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了
     752         
     753         mov eax,0x00000000
     754         mov ebx,0x000fffff
     755         mov ecx,0x00c0d200                                 ;4KB粒度的堆栈段描述符,特权级2
     756         call Sys_Routine_Segement:Make_Seg_Descriptor
     757         mov ebx,esi
     758         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     759         or cx,0x0002                                    ;选择子特权级为2
     760         
     761         mov [es:edi+24],cx                                ;cx是数据段的选择子
     762         mov edx,[es:esi+0x06]                            ;向上拓展的ESP2的初始值
     763         mov [es:edi+20],edx        
     764         
     765         ;现在开始重定位API符号表
     766         ;---------------------------------------------------------------------
     767         mov eax,All_4GB_Segment            ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问
     768         mov es,eax
     769         mov eax,Core_Data_Segement
     770         mov ds,eax
     771         
     772         cld
     773         mov ecx,[es:0x0c]
     774         mov edi,[es:0x08]
     775 
     776         _loop_U_SALT:                    
     777             push edi
     778             push ecx
     779             
     780             mov ecx,salt_items_sum
     781             mov esi,salt
     782             
     783             _loop_C_SALT:
     784                 push edi
     785                 push esi
     786                 push ecx
     787                 
     788                 mov ecx,64                ;比较256个字节
     789                 repe cmpsd
     790                 jne _re_match            ;如果成功匹配,那么esi和edi刚好会在数据区之后的
     791                 
     792                 mov eax,[esi]            ;偏移地址
     793                 mov [es:edi-256],eax    ;把偏移地址填入用户程序的符号区
     794                 mov ax,[esi+0x04]        ;段的选择子
     795                 
     796                 or ax,0x0002            ;把RPL改为3,代表(内核)赋予应用程序以特权级3
     797                 mov [es:edi-252],ax        ;把段的选择子填入用户程序的段选择区
     798                 
     799                 _re_match:
     800                 pop ecx
     801                 pop esi
     802                 add esi,salt_length
     803                 pop edi
     804             loop _loop_C_SALT
     805             
     806             pop ecx
     807             pop edi
     808             add edi,256
     809         loop _loop_U_SALT
     810         ;---------------------------------------------------------------------
     811         ;----------------------填入临时中转任务门选择子-----------------------
     812         mov ax,[salt_tp]
     813         mov [es:0x14],ax            ;填充任务门选择子
     814         ;---------------------------------------------------------------------
     815         mov esi,[ebp+11*4]                        ;重新获得TCB的线性基地址
     816         
     817         ;在GDT中存入LDT信息
     818         mov eax,[es:esi+0x0c]
     819         movzx ebx,word[es:esi+0x0a]
     820         mov ecx,0x00408200                        ;LDT描述符,特权级0级
     821         call Sys_Routine_Segement:Make_Seg_Descriptor
     822         call Sys_Routine_Segement:Set_New_GDT
     823         mov [es:esi+0x10],cx                    ;在TCB放入LDT选择子
     824         
     825         ;构建TSS剩下的信息表
     826         mov ebx,[es:esi+0x14]
     827         mov [es:ebx+96],cx                        ;TSS中LDT选择子
     828         
     829         mov word[es:ebx+0],0                    ;填充反向链(任务切换的时候处理器会帮着填的,不用操心)
     830         mov dx,[es:esi+0x12]                    ;TSS段界限
     831         mov [es:ebx+102],dx
     832         mov word[es:ebx+100],0                    ;T=0
     833       
     834         mov eax,[es:0x04]                          ;从任务的4GB地址空间获取入口点 
     835         mov [es:ebx+32],eax                        ;填写TSS的EIP域 
     836         
     837         pushfd
     838         pop edx
     839         mov [es:ebx+36],edx                           ;EFLAGS 
     840         
     841         ;在GDT中存入TSS信息
     842         mov eax,[es:esi+0x14]
     843         movzx ebx,word[es:esi+0x12]
     844         mov ecx,0x00408900
     845         call Sys_Routine_Segement:Make_Seg_Descriptor
     846         call Sys_Routine_Segement:Set_New_GDT
     847         mov [es:esi+0x18],cx
     848         
     849         ;复制一份页表
     850         call Sys_Routine_Segement:Copy_Page
     851         mov ebx,[es:esi+0x14]
     852         mov [es:ebx+28],eax                        ;填写PDBR(CR3)
     853         
     854         pop es
     855         pop ds
     856         popad
     857         ret 8                                    ;相当于是stdcall,过程清栈
     858         ;---------------------------------------------------------------------
     859     start:
     860         mov eax,Core_Data_Segement
     861         mov ds,eax
     862         mov eax,All_4GB_Segment
     863         mov es,eax
     864         
     865         mov ebx,message_1
     866         call Sys_Routine_Segement:put_string
     867         
     868         ;下面准备开启页管理
     869         mov ecx,1024
     870         mov ebx,PDT_Mem_Address
     871         xor esi,esi
     872         
     873         _flush_PDT:                                ;清空页表
     874             mov dword[es:ebx+esi*4],0x00000000    
     875             inc esi
     876         loop _flush_PDT
     877         
     878         ;下面的代码会非常绕,注意观察
     879         ;页目录的最后一个32字节是指向自己的页表(这个页表就是页目录)
     880         mov dword[es:ebx+4092],PDT_Mem_Address
     881         or dword[es:ebx+4092],0x00000007        ;属性:存在于物理内存,可写可读,啥程序都能访问
     882         
     883         ;页目录的第一个页表指示最底下1MB内存的4KB页(内核代码,必须虚拟地址和物理地址一致)
     884         mov dword[es:ebx+0],PDT_Mem_Address
     885         or dword[es:ebx+0],0x00000007            ;属性:存在于物理内存,可写可读,啥程序都能访问
     886         or dword[es:ebx+0],0x00001000            ;注意是这个页表是放在目录的后一个页!
     887         
     888         ;现在0x00020000的页是第0个页表(指示页目录),0x00021000是第一个页表(指示底下1MB的东西)
     889         mov ebx,PDT_Mem_Address
     890         or ebx,0x00001000
     891         xor eax,eax
     892         xor esi,esi
     893         
     894         _make_page:
     895             mov edx,eax
     896             or edx,0x00000007                    ;属性:存在于物理内存,可写可读,啥程序都能访问
     897             mov [es:ebx+esi*4],edx                ;物理位置
     898             add eax,0x1000
     899             inc esi
     900             cmp esi,256
     901         jl _make_page
     902         
     903         _make_page_last:
     904             mov dword[es:ebx+esi*4],0x00000000    ;标记页为无效
     905             inc esi
     906             cmp esi,1024
     907         jl _make_page_last
     908         
     909         mov eax,PDT_Mem_Address
     910         mov cr3,eax                                ;把页目录基地址放在cr3,准备开启页功能
     911         
     912         mov eax,cr0
     913         or eax,0x80000000
     914         mov cr0,eax                                ;置PG位,开启页功能
     915         
     916         ;--------------------------已开启页功能--------------------------------
     917         ;------------------开始映射高端内存区到页目录表中----------------------
     918         mov ebx,0xfffff000                                    ;表示页表指向页目录自己
     919         mov esi,Global_Page_Directory                        ;映射的起始地址
     920         shr esi,22
     921         shl esi,2
     922         mov dword[es:ebx+esi],PDT_Mem_Address                
     923         add dword[es:ebx+esi],0x00001003
     924         
     925         sgdt [pgdt_base_tmp]
     926         mov ebx,[pgdt_base_tmp+2]                            ;GDT线性基地址
     927         
     928         or dword[es:ebx+0x10+4],Global_Page_Directory        ;0x10是刚好是64个字节,忽略0字串
     929         or dword[es:ebx+0x18+4],Global_Page_Directory
     930         or dword[es:ebx+0x20+4],Global_Page_Directory
     931         or dword[es:ebx+0x28+4],Global_Page_Directory
     932         or dword[es:ebx+0x30+4],Global_Page_Directory
     933         or dword[es:ebx+0x38+4],Global_Page_Directory
     934         
     935         add dword[pgdt_base_tmp+2],Global_Page_Directory    ;线性基地址也要变
     936         
     937         lgdt [pgdt_base_tmp]
     938         jmp Core_Code_Segement:_flush                        ;强制刷新代码段,映射到高端内存区
     939         
     940         _flush:
     941         mov eax,Stack_Segement
     942         mov ss,eax
     943         
     944         mov eax,Core_Data_Segement
     945         mov ds,eax
     946         
     947         mov ebx,message_page
     948         call Sys_Routine_Segement:put_string
     949         ;----------------------------------------------------------------------
     950         
     951         _@load:
     952         ;----------------------------安装门------------------------------------
     953         mov edi,salt
     954         mov ecx,salt_items_sum
     955         _set_gate:
     956             push ecx
     957             mov eax,[edi+256]
     958             mov bx,[edi+260]        ;选择子
     959             mov cx,0xec00            ;门是特权级是3的门,那么任何程序都能调用
     960             or cx,[edi+262]            ;加上参数个数
     961             
     962             call Sys_Routine_Segement:Make_Gate_Descriptor
     963             call Sys_Routine_Segement:Set_New_GDT
     964             mov [edi+260],cx        ;回填选择子
     965             add edi,salt_length
     966             pop ecx
     967         loop _set_gate
     968         ;----------------------------------------------------------------------
     969         mov ebx,message_In_Gate
     970         call far [salt_1+256]            ;调用门显示字符信息(忽略偏移地址(前4字节))
     971         
     972         mov ebx,[core_next_laddr]
     973         call Sys_Routine_Segement:alloc_inst_a_page
     974         add dword[core_next_laddr],4096        ;指向下一个页
     975         
     976         mov word[es:ebx+100],0             ;TI=0
     977         mov word[es:ebx+102],103        ;任务管理器不需要I/O映射,要大于等于界限
     978         mov word[es:ebx+96],0            ;任务允许没有自己的LDT
     979         mov eax,cr3
     980         mov dword[es:ebx+28],eax        ;设置CR3,注意不是0了!    
     981         mov word[es:ebx+0],0            ;没有前一个任务
     982         
     983         mov eax,ebx
     984         mov ebx,103                        ;TSS段界限
     985         mov ecx,0x00408900
     986         call Sys_Routine_Segement:Make_Seg_Descriptor
     987         call Sys_Routine_Segement:Set_New_GDT
     988         mov [prgman_tss+0x04],cx
     989         
     990         ltr    cx                            ;启动任务
     991         ;------------------安装用户管理程序的临时返回任务门--------------------        
     992         mov eax,0x0000                    ;TSS不需要偏移地址
     993         mov bx,[prgman_tss+0x04]        ;TSS的选择子
     994         mov cx,0xe500
     995         
     996         call Sys_Routine_Segement:Make_Gate_Descriptor        
     997         call Sys_Routine_Segement:Set_New_GDT
     998         mov [salt_tp],cx                ;填入临时中转任务门选择子,注意不需要加260了
     999         ;----------------------------------------------------------------------
    1000         mov ebx,[core_next_laddr]
    1001         call Sys_Routine_Segement:alloc_inst_a_page
    1002         add dword[core_next_laddr],4096        ;指向下一个页
    1003         
    1004         mov dword[es:ebx+0x06],0         ;用户程序从0位置开始分配
    1005         mov word[es:ebx+0x0a],0xffff    ;LDT初始界限
    1006         mov ecx,ebx                        ;添加到TCB链中
    1007         call append_to_tcb
    1008         
    1009         push dword User_Program_AddressA
    1010         push ecx
    1011         
    1012         call load_program
    1013         
    1014         mov ebx,task_switch
    1015         call Sys_Routine_Segement:put_string
    1016         
    1017         jmp far [es:ecx+0x14]
    1018         ;call far [es:ecx+0x14]
    1019         
    1020         mov ebx,core_stop
    1021         call Sys_Routine_Segement:put_string
    1022         
    1023         hlt
    1024         ;----------------------------------------------------------------------
    1025 ;=========================================================================
    1026 SECTION core_trail
    1027 ;----------------------------------------------------------------
    1028 Program_end:
     1 ;================================用户程序=======================================
     2         program_length               dd program_end          ;程序总长度#0x00
     3         entry_point                  dd start                ;程序入口点#0x04
     4         salt_position                dd salt_begin           ;SALT表起始偏移量#0x08 
     5         salt_items                   dd (salt_end-salt_begin)/256 
     6                                                             ;SALT条目数#0x0C
     7         TpBack:                         dd  0                    ;任务门的偏移地址没用,直接填充就可以了
     8                                     dw    0                    ;任务门的选择子#0x14
     9         Own_Page                    dd    0                    ;自己页面的物理地址#0x16
    10 ;-------------------------------------------------------------------------------
    11         ;符号地址检索表
    12         salt_begin:                                     
    13         PrintString                  db  '@Printf'
    14                             times 256-($-PrintString) db 0
    15         TerminateProgram:            db  '@TerminateProgram'
    16                             times 256-($-TerminateProgram) db 0
    17 ;-------------------------------------------------------------------------------
    18         reserved              times 256*500 db 0            ;保留一个空白区,以演示分页
    19 ;-------------------------------------------------------------------------------
    20         ReadDiskData                 db  '@ReadHarddisk'
    21                             times 256-($-ReadDiskData) db 0
    22         PrintDwordAsHex              db  '@PrintDwordAsHexString'
    23                             times 256-($-PrintDwordAsHex) db 0
    24         salt_end:
    25         message_0                      db  0x0d,0x0a,
    26                                     db  '  ............User task is running with '
    27                                     db  'paging enabled!............',0x0d,0x0a,0
    28         message_1                      db  0x0d,0x0a,
    29                                     db  '  ..........,,,..The address of the user'
    30                                     db  ' task(first 50)!...........',0x0d,0x0a,0                            
    31         space                         db  0x20,0x20,0 
    32         next_line:                    db  0x0d,0x0a,0
    33         Own_Page_Message            db  0x0d,0x0a,
    34                                     db    0x20,0x20,'Task Page: ',0
    35 ;-------------------------------------------------------------------------------
    36       [bits 32]
    37 ;-------------------------------------------------------------------------------
    38 start:
    39         ;--------------------------显示50个页内容------------------------
    40         mov ebx,message_0
    41         call far[PrintString]
    42          
    43         xor esi,esi
    44         mov ecx,50
    45         .b1:
    46             mov ebx,space
    47             call far[PrintString] 
    48          
    49             mov edx,[esi*4]
    50             call far[PrintDwordAsHex]
    51             inc esi
    52         loop .b1
    53     
    54         ;--------------------显示新任务页目录地址--------------------------
    55         mov ebx,Own_Page_Message
    56         call far[PrintString]     
    57         mov ebx,space
    58         call far [PrintString]
    59         mov ebx,[Own_Page]
    60         mov edx,[es:ebx]
    61         and edx,0xfffff000            ;处理器会设置A和D位,直接忽略得到物理地址了
    62         call far[PrintDwordAsHex]
    63         
    64         mov ebx,next_line
    65         call far [PrintString]
    66         ;--------------------显示任务前50个页物理地址----------------------
    67         mov ebx,message_1
    68         call far[PrintString]         
    69         mov edi,0xffc00000        
    70         mov ecx,50
    71         xor esi,esi
    72                             
    73         _show:
    74             mov ebx,space
    75             call far [PrintString]
    76             
    77             mov edx,[es:edi+esi*4]
    78             and edx,0xfffff000            ;处理器会设置A和D位,直接忽略得到物理地址了
    79             call far[PrintDwordAsHex]
    80             inc esi
    81         loop _show
    82         jmp far [fs:TpBack]
    83         ;iretd
    84 ;-------------------------------------------------------------------------------
    85 program_end:
  • 相关阅读:
    test20190805 夏令营NOIP训练20
    test20190803 夏令营NOIP训练19
    test20190802 夏令营NOIP训练18
    「SDOI2016」征途
    LG4195 【模板】exBSGS
    test20190731 夏令营NOIP训练16
    CF600E Lomsat gelral 和 CF741D Dokhtar-kosh paths
    CF623E Transforming Sequence
    LG4351 [CERC2015]Frightful Formula
    CF553E Kyoya and Train
  • 原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/5317979.html
Copyright © 2020-2023  润新知