分页机制代码详细注释

来源:互联网 发布:双层玻璃杯 知乎 编辑:程序博客网 时间:2024/04/26 06:33

分页的代码红色标记,其它的在前面“保护模式跳转中”已经解释过。

%include "pm.inc"

org        0100h
        jmp    LABEL_BEGIN
PageDirBase        equ    200000h    ; 页目录开始地址: 2M
PageTblBase        equ    201000h    ; 页表开始地址: 2M+4K

[SECTION .gdt]
;GDT
LABEL_GDT:        Descriptor      0,         0,    0
LABEL_DESC_NORMAL:    Descriptor      0,        0ffffh,    DA_DRW
LABEL_DESC_CODE32:    Descriptor      0,SegCode32Len-1,    DA_C + DA_32
LABEL_DESC_CODE16:    Descriptor      0,        0ffffh,    DA_C
LABEL_DESC_DATA:    Descriptor      0,     DataLen-1,    DA_DRW
LABEL_DESC_STACK:    Descriptor       0,    TopOfStack,    DA_DRWA + DA_32
LABEL_DESC_VIDEO:    Descriptor  0B8000h,        0ffffh,    DA_DRW

LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW;Page Directory
LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K;Page Tables
                ;此时G位置,界限粒度为4K,所以1023表示0-1023的1024个4K

;End od GDT


GdtLen            equ        $ - LABEL_GDT
GdtPtr            dw        GdtLen - 1
            dd        0


SelectorNormal        equ        LABEL_DESC_NORMAL      - LABEL_GDT
SelectorCode32        equ        LABEL_DESC_CODE32     - LABEL_GDT
SelectorCode16        equ        LABEL_DESC_CODE16     - LABEL_GDT
SelectorData        equ        LABEL_DESC_DATA     - LABEL_GDT
SelectorStack        equ        LABEL_DESC_STACK     - LABEL_GDT
SelectorVideo        equ        LABEL_DESC_VIDEO     - LABEL_GDT
SelectorPageDir        equ        LABEL_DESC_PAGE_DIR    - LABEL_GDT
SelectorPageTbl        equ        LABEL_DESC_PAGE_TBL    - LABEL_GDT


[SECTION .data1]
ALIGN    32
[BITS 32]
LABEL_DATA:
    SPValueRealMode        dw    0
    PMMessage:        db    "In Protect Mode now. ^_^",0
    OffsetPMMessage        equ    PMMessage - $$
    DataLen            equ    $ - LABEL_DATA


[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
    times 512 db 0
    TopOfStack        equ    $ - LABEL_STACK - 1   




[SECTION .s16]
[BITS 16]

LABEL_BEGIN:
    mov         ax,cs
    mov        ds,ax
    mov        es,ax
    mov        ss,ax
    mov        sp,0100h
    mov        [LABEL_GO_BACK_TO_REAL + 3],ax
    mov        [SPValueRealMode],sp

    ;初始化16位段描述符
    ;xor        eax,eax
    mov        ax,cs
    movzx        eax,ax
    shl        eax,4
    add        eax,LABEL_SEG_CODE16
    mov        word [LABEL_DESC_CODE16 + 2],ax
    shr        eax,16
    mov        byte [LABEL_DESC_CODE16 + 4],al
    mov        byte [LABEL_DESC_CODE16 + 7],ah

    ;初始化32伴段描述符
        xor             eax,eax
        mov             ax,cs
        shl             eax,4
        add             eax,LABEL_SEG_CODE32
        mov             word [LABEL_DESC_CODE32 + 2],ax
        shr             eax,16
        mov             byte [LABEL_DESC_CODE32 + 4],al
        mov             byte [LABEL_DESC_CODE32 + 7],ah

    ;初始化数据段描述符
        xor             eax,eax
        mov             ax,ds
        shl             eax,4
        add             eax,LABEL_DATA
        mov             word [LABEL_DESC_DATA + 2],ax
        shr             eax,16
        mov             byte [LABEL_DESC_DATA + 4],al
        mov             byte [LABEL_DESC_DATA + 7],ah

    ;初始化堆栈段描述符
        xor             eax,eax
        mov             ax,ds
        shl             eax,4
        add             eax,LABEL_STACK
        mov             word [LABEL_DESC_STACK + 2],ax
        shr             eax,16
        mov             byte [LABEL_DESC_STACK + 4],al
        mov             byte [LABEL_DESC_STACK + 7],ah


    xor        eax,eax
    mov        ax,ds
    shl        eax,4
    add        eax,LABEL_GDT
    mov        dword [GdtPtr + 2],eax

    ;加载 gdt   
    lgdt        [GdtPtr]
   
    ;关中断
    cli

    ;打开A20地址线
    in        al,92h
    or        al,00000010b
    out        92h,al
   
    ;set the cr0
    mov        eax,cr0
    or        eax,1
    mov        cr0,eax
   
    jmp        dword SelectorCode32:0
;End of [SECTION .16]   

LABEL_REAL_ENTRY:
    mov        ax,cs
    mov        ds,ax
    mov        es,ax
    mov        ss,ax
    mov        sp,[SPValueRealMode]
   
    in        al,92h
    and        al,11111101b
    out        92h,al
   
    sti
    mov        ax,4c00h
    int        21h



[SECTION .s32]
[BITS 32]

LABEL_SEG_CODE32:   
    call        SetupPaging
    mov         ax,SelectorData
    mov        ds,ax
    mov        ax,SelectorVideo
    mov        gs,ax
    mov        ax,SelectorStack
    mov        ss,ax
   
    mov        esp,TopOfStack

    mov        ah,0Ch
    xor        esi,esi
    xor        edi,edi
    mov        esi,OffsetPMMessage
    mov        edi,(80 * 10 + 0) * 2
    cld

.1:
    lodsb
    test        al,al
    jz        .2
    mov        [gs:edi],ax
    add        edi,2
    jmp        .1

.2:

    jmp        SelectorCode16:0

SetupPaging:
   
    ;初始化页目录
    mov        ax,SelectorPageDir    ;
    mov        es,ax            ;将PageDir段首个加载到es,后面的stosd会从es:edi开始。
    mov        ecx,1024        ;循环控制标量
    xor        edi,edi
    xor        eax,eax
    mov        eax,PageTblBase|PG_P|PG_USU|PG_RWW
                        ;PDE中前20位存放的是页表的地址,第一个PDE放的正好是PageTblBase
                        ;的基址。如果PTE连续放置的话,下面每个PDE对应的地址就是PageTblBase+4K*索引
.1:
    stosd                    ;将eax的内容存储到es:edi的位置后,di=di+4, 所以每次存储后自动跳过正好是下一个
                        ;PDE的位置。而内容是下一个页表的地址,所以只需要将上一个页表地址加4096.
    add        eax,4096
    loop        .1            ;每次执行loop指令,cx减1,然后判断cx是否等于0,如果不为0则转移到loop指令后的标号处,
                        ;实现循环;如果为0顺序执行。
    ;初始化页表

    mov        ax,SelectorPageTbl    ;
    mov        es,ax            ;同上,将页表的首地址加载到es,后面的stosd会从es:edi开始
    mov         ecx,1024*1024        ;按20位(PTE的前20位)表示的最大值来计算所需要初始化的页表个数。
    xor        edi,edi
    xor        eax,eax
    mov        eax,PG_P|PG_USU|PG_RWW    ;这里为了使用线性地址和物理地址做绝对映射,所以第1个PTE的首地址被初始化为物理地址的0位置。
.2:
    stosd
    add        eax,4096        ;作用同上
    loop        .2            ;

    mov        eax,PageDirBase
    mov        cr3,eax            ;将PageDirBase的地址加载到cr3
    mov        eax,cr0
    or         eax,80000000h
    mov        cr0,eax            ;将cr0的PG位置1使分页生效。
    jmp        short    .3

.3:    nop
    ret

SegCode32Len        equ    $ - LABEL_SEG_CODE32

[SECTION .s16code]
ALIGN    32
[BITS 16]
LABEL_SEG_CODE16:
    mov        ax,SelectorNormal
    mov        ds,ax
    mov        es,ax
    mov        fs,ax
    mov        gs,ax
    mov        ss,ax
   
    mov        eax,cr0
    and        eax, 7FFFFFFEh        ; 跳回实模式后一定要将CR0复位。与上面的 "or  eax,80000000h; mov   cr0,eax" 对应PE=0, PG=0
    mov        cr0,eax
   
LABEL_GO_BACK_TO_REAL:
    jmp        0:LABEL_REAL_ENTRY

Code16Len         equ     $ - LABEL_SEG_CODE16

 

 

---------------------------------------------------------------------------------

 下面是根据内存动态计算页表个数的代码

%include   "pm.inc"
PageDirBase  equ  200000h
PageTblBase  equ  201000h

org  0100h
  jmp LABEL_BEGIN

[SECTION .gdt]
;GDT
LABEL_GDT:  Descriptor    0,   0, 0
LABEL_DESC_NORMAL: Descriptor    0,     0ffffh, DA_DRW
LABEL_DESC_PAGE_DIR: Descriptor    PageDirBase,       4095, DA_DRW
LABEL_DESC_PAGE_TBL: Descriptor    PageTblBase,    4096 * 8 - 1,  DA_DRW  ; Page Tables,这里只是一个估算
LABEL_DESC_CODE32: Descriptor    0,  SegCode32Len-1, DA_C + DA_32
LABEL_DESC_CODE16: Descriptor    0,     0ffffh, DA_C
LABEL_DESC_VIDEO: Descriptor     0B8000h,     0ffffh, DA_DRW
LABEL_DESC_DATA:      Descriptor   0,       DataLen-1,  DA_DRW  ; Data
LABEL_DESC_STACK:     Descriptor   0,      TopOfStack,  DA_DRWA + DA_32 ; Stack, 32 位
;End od GDT

GdtLen   equ  $ - LABEL_GDT
GdtPtr   dw  GdtLen - 1
   dd  0
SelectorNormal  equ  LABEL_DESC_NORMAL   - LABEL_GDT
SelectorPageDir  equ  LABEL_DESC_PAGE_DIR - LABEL_GDT
SelectorPageTbl  equ  LABEL_DESC_PAGE_TBL - LABEL_GDT
SelectorCode32  equ  LABEL_DESC_CODE32  - LABEL_GDT
SelectorCode16  equ  LABEL_DESC_CODE16  - LABEL_GDT
SelectorVideo  equ  LABEL_DESC_VIDEO  - LABEL_GDT
SelectorData  equ  LABEL_DESC_DATA  - LABEL_GDT
SelectorStack  equ  LABEL_DESC_STACK - LABEL_GDT

 


[SECTION .data1]  ; 数据段
ALIGN 32
[BITS 32]
LABEL_DATA:

;字符串定义
_szPMMessage:  db "In Protect Mode now. ^-^", 0Ah, 0Ah, 0 ; 进入保护模式后显示此字符串
_szMemChkTitle:  db "BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0 ; 进入保护模式后显示此字符串
_szRAMSize  db "RAM size:",0
_szReturn  db 0Ah,0

;变量,这些变量是在实模式下调用中断15时用到的,使用的是物理地址。

_wSPValueInRealMode dw 0
_dwMCRNumber:  dd 0
_dwDispPos:  dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 0 列。
_dwMemSize:  dd 0
_ARDStruct:  ;Address Range Descriptor Strucure
 _dwBaseAddrLow:  dd 0
 _dwBaseAddrHigh: dd 0
 _dwLengthLow:  dd 0
 _dwLengthHigh:  dd 0
 _dwType:  dd 0

_MemChkBuf: times 256 db 0

; 当上面的变量转换到保护模式后,所以要把变量的绝对地址换算成偏移地址。即物理
; 地址减速去段基址。这是段基址$$的一个非常好的应用。
; 保护模式下使用这些符号

szPMMessage  equ _szPMMessage - $$
szMemChkTitle  equ _szMemChkTitle - $$
szRAMSize  equ _szRAMSize - $$
szReturn  equ _szReturn - $$
dwDispPos  equ _dwDispPos - $$
dwMemSize  equ _dwMemSize - $$
dwMCRNumber  equ _dwMCRNumber - $$
ARDStruct  equ _ARDStruct - $$
 dwBaseAddrLow equ _dwBaseAddrLow - $$
 dwBaseAddrHigh equ _dwBaseAddrHigh - $$
 dwLengthLow equ _dwLengthLow - $$
 dwLengthHigh equ _dwLengthHigh - $$
 dwType  equ _dwType  - $$
MemChkBuf  equ _MemChkBuf - $$

DataLen   equ $ - LABEL_DATA

[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
 times 512 db 0
 TopOfStack  equ $ - LABEL_STACK - 1 

 

 


[SECTION .s16]
[BITS 16]

LABEL_BEGIN:
 mov   ax,cs
 mov  ds,ax
 mov  es,ax
 mov  ss,ax
 mov  sp,0100h
 mov  [LABEL_GO_BACK_TO_REAL + 3],ax
 mov  [_wSPValueInRealMode],sp
   

 ;利用int 15获取内存信息时,每次调用时要填充以下信息:
 ; eax:0E820h, 固定的int 15的获取内存信息的功能号
 ; ebx:是下一个内存块地址描述符的后续标记,第一次开始时要求为0.
 ;     然后每次调用前将上次返回值原样放入ebx(即不要修改ebx),只到
 ;     ebx的值为0时说明且CF没有置1, 说明是最后一次的内存描述符。
 ;     (如果ebx为0而CF=1,有可能是第一次调用时就出错)
 ; es:di:指向地址范围的描述描述符结构,调用且BIOS会将内存信息的填充到这个结构中。 
 ; ecx:为es:di指向的地址范围描述符结构的大小。即使es:di指向的结构大于这个数,BIOS
 ;     也只填充ecx指定大小的字节,而有些BIOS只填充20个字节,所以一般ecx指定20
 ; edx:0534D4150h,固定的字符标记('SMAP')
 ;调用后:
 ; CF=0,正常。CF=1,错误。
 ; eax:0534D4150h
 ; es:di  返回传入的地址范围的描述符结构
 ; ecx:返回填充到上述结构中的字节数 
 ; ebx:下一个地址描述符所需要的后续标记,如果为0且CF=0则当前为最后一个。如果不为0则
 ; 在继续调用下一次中断时把此值填充,即不要修改上一次返回的值。

 mov ebx, 0   ;首次为0,以后由 BIOS填充并保持给下一次调用,程序不要修改。
 mov di, _MemChkBuf  ;准备由BIOS返回的地址范围描述符填充的结构首地址,每次调用后增加ecx指定的大小供下一次填充。
.loop:
 mov eax, 0E820h  ;
 mov ecx, 20   ;
 mov edx, 0534D4150h  ;
 int 15h   ;上以四行按int 15的0534D4150号功能要求填入参数。
 jc LABEL_MEM_CHK_FAIL ;如果CF被置位,由跳转到LABEL_MEM_CHK_FAIL
 add di, 20   ;地址步进20为下一个结构地址。
 inc dword [_dwMCRNumber] ;每调用一次将_dwMCRNumber加1作为循环计数。
 cmp ebx, 0   ;
 jne .loop   ;如果ebx为0则跳出循环。
 jmp LABEL_MEM_CHK_OK ;
LABEL_MEM_CHK_FAIL:
 mov dword [_dwMCRNumber], 0 ;如果是CF=1跳入这里,说明调用错,将_dwMCRNumber置0
LABEL_MEM_CHK_OK:
 ;调用完成后,_MemChkBuf中保存了_dwMCRNumber个20字节大小的结构,所以_MemChkBuf大小有可能不够用。要有一定的预估。

 ;初始化16位段描述符
 ;xor  eax,eax
 mov  ax,cs
 movzx  eax,ax
 shl  eax,4
 add  eax,LABEL_SEG_CODE16
 mov  word [LABEL_DESC_CODE16 + 2],ax
 shr  eax,16
 mov  byte [LABEL_DESC_CODE16 + 4],al
 mov  byte [LABEL_DESC_CODE16 + 7],ah

 ;初始化32位段描述符
        xor             eax,eax
        mov             ax,cs
        shl             eax,4
        add             eax,LABEL_SEG_CODE32
        mov             word [LABEL_DESC_CODE32 + 2],ax
        shr             eax,16
        mov             byte [LABEL_DESC_CODE32 + 4],al
        mov             byte [LABEL_DESC_CODE32 + 7],ah

 ; 初始化数据段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_DATA
 mov word [LABEL_DESC_DATA + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_DATA + 4], al
 mov byte [LABEL_DESC_DATA + 7], ah

 ; 初始化堆栈段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_STACK
 mov word [LABEL_DESC_STACK + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_STACK + 4], al
 mov byte [LABEL_DESC_STACK + 7], ah


 xor  eax,eax
 mov  ax,ds
 shl  eax,4
 add  eax,LABEL_GDT
 mov  dword [GdtPtr + 2],eax

 ;加载 gdt 
 lgdt  [GdtPtr]
 
 ;关中断
 cli

 ;打开A20地址线
 in  al,92h
 or  al,00000010b
 out  92h,al
 
 ;set the cr0
 mov  eax,cr0
 or  eax,1
 mov  cr0,eax
 
 jmp  dword SelectorCode32:0
;End of [SECTION .16] 

LABEL_REAL_ENTRY:
 mov  ax,cs
 mov  ds,ax
 mov  es,ax
 mov  ss,ax
 mov  sp,[_wSPValueInRealMode]
 
 in  al,92h
 and  al,11111101b
 out  92h,al
 
 sti
 mov  ax,4c00h
 int  21h

 

[SECTION .s32]
[BITS 32]

LABEL_SEG_CODE32: 

 mov   ax,SelectorData
 mov  ds,ax
 mov  es, ax;SelectorData传给es, 是因为在下面的串操作中需要使用es,而为了访问在同一段内访问串,所以将es的值ds的值相等,

                   ;指象同一段描述符
 mov  ax,SelectorVideo ;
 mov  gs,ax
 mov  ax,SelectorStack
 mov  ss,ax
 mov  esp,TopOfStack

 

 ; 下面显示一个字符串
 push szPMMessage
 call DispStr
 add esp, 4

 push szMemChkTitle
 call DispStr
 add esp, 4

 call DispMemSize  ; 显示内存信息
 call SetupPaging  ; 启动分页机制
 jmp  SelectorCode16:0

; 启动分页机制 --------------------------------------------------------------
SetupPaging:
 ; 根据内存大小计算应初始化多少PDE以及多少页表
 xor edx, edx ;div指令一定要清空edx,否则极容易产生非法操作.
 mov eax, [dwMemSize]
 mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
 div ebx  ; div的商返回到eax,余数返回到edx
 mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
 test edx, edx
 jz .no_remainder
 inc ecx  ; 如果余数不为 0 就需增加一个页表
.no_remainder:
 push ecx  ; 暂存页表个数

 ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.

 ; 首先初始化页目录
 mov ax, SelectorPageDir ; 此段首地址为 PageDirBase
 mov es, ax
 xor edi, edi
 xor eax, eax
 mov eax, PageTblBase | PG_P  | PG_USU | PG_RWW
.1:
 stosd
 add eax, 4096  ; 为了简化, 所有页表在内存中是连续的.
 loop .1

 ; 再初始化所有页表
 mov ax, SelectorPageTbl ; 此段首地址为 PageTblBase
 mov es, ax
 pop eax   ; 页表个数
 mov ebx, 1024  ; 每个页表 1024 个 PTE
 mul ebx
 mov ecx, eax  ; PTE个数 = 页表个数 * 1024
 xor edi, edi
 xor eax, eax
 mov eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 add eax, 4096  ; 每一页指向 4K 的空间
 loop .2

 mov eax, PageDirBase
 mov cr3, eax
 mov eax, cr0
 or eax, 80000000h
 mov cr0, eax
 jmp short .3
.3:
 nop

 ret
; 分页机制启动完毕 ----------------------------------------------------------

 

DispMemSize:
 push esi
 push edi
 push ecx

 mov esi, MemChkBuf
 mov ecx, [dwMCRNumber];for(int i=0;i<[MCRNumber];i++)//每次得到一个ARDS
.loop:      ;{
 mov edx, 5    ;  for(int j=0;j<5;j++) //每次得到一个ARDS中的成员
 mov edi, ARDStruct   ;  {//依次显示BaseAddrLow,BaseAddrHigh,LengthLow,
.1:      ;             LengthHigh,Type
 push dword [esi]   ;
 call DispInt    ;    DispInt(MemChkBuf[j*4]); //显示一个成员
 pop eax    ;
 stosd     ;    ARDStruct[j*4] = MemChkBuf[j*4];
 add esi, 4    ;
 dec edx    ;
 cmp edx, 0    ;
 jnz .1    ;  }
 call DispReturn   ;  printf("/n");
 cmp dword [dwType], 1 ;  if(Type == AddressRangeMemory)
 jne .2    ;  {
 mov eax, [dwBaseAddrLow];
 add eax, [dwLengthLow];
 cmp eax, [dwMemSize]  ;    if(BaseAddrLow + LengthLow > MemSize)
 jb .2    ;
 mov [dwMemSize], eax  ;    MemSize = BaseAddrLow + LengthLow;
.2:      ;  }
 loop .loop    ;}
      ;
 call DispReturn   ;printf("/n");
 push szRAMSize   ;
 call DispStr    ;printf("RAM size:");
 add esp, 4    ;
      ;
 push dword [dwMemSize] ;
 call DispInt    ;DispInt(MemSize);
 add esp, 4    ;

 pop ecx
 pop edi
 pop esi
 ret

%include "lib.inc" ; 库函数
SegCode32Len  equ $ - LABEL_SEG_CODE32

[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
 mov  ax,SelectorNormal
 mov  ds,ax
 mov  es,ax
 mov  fs,ax
 mov  gs,ax
 mov  ss,ax
 
 mov  eax,cr0
 and  eax, 7FFFFFFEh  ; PE=0, PG=0and  al,11111110b
 mov  cr0,eax
 
LABEL_GO_BACK_TO_REAL:
 jmp  0:LABEL_REAL_ENTRY

Code16Len   equ  $ - LABEL_SEG_CODE16

 

原创粉丝点击