现在的位置: 首页 > 综合 > 正文

保护模式 页式存储

2018年01月24日 ⁄ 综合 ⁄ 共 5826字 ⁄ 字号 评论关闭

; 启动分页机制 --------------------------------------------------------------
SetupPaging:
 ; 根据内存大小计算应初始化多少PDE以及多少页表
 xor edx, edx
 mov eax, [dwMemSize]
 mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
 div ebx
 mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
 test edx, edx
 jz .no_remainder
 inc ecx  ; 如果余数不为 0 就需增加一个页表
.no_remainder:
 mov [PageTableNumber], ecx ; 暂存页表个数

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

 ; 首先初始化页目录
 mov ax, SelectorFlatRW
 mov es, ax
 mov edi, PageDirBase0 ; 此段首地址为 PageDirBase0
 xor eax, eax
 mov eax, PageTblBase0 | PG_P  | PG_USU | PG_RWW
.1:
 stosd    
 ;将 eax 中的内容保存到 es:(e)di 处, (e)di 再加 4,即[PageTbleBase0](table base0的地址)->SelectorFlatRW:PageDirBase0
 ;其功能初始化SelectorFlatRW:PageDirBase0的dir表

 add eax, 4096  ; 为了简化, 所有页表在内存中是连续的.
 loop .1

 ; 再初始化所有页表
 mov eax, [PageTableNumber] ; 页表个数
 mov ebx, 1024  ; 每个页表 1024 个 PTE
 mul ebx
 mov ecx, eax  ; PTE个数 = 页表个数 * 1024
 mov edi, PageTblBase0 ; 此段首地址为 PageTblBase0
 xor eax, eax
 mov eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 ;其功能初始化SelectorFlatRW:PageTblBase0的tbl表
 add eax, 4096  ; 每一页指向 4K 的空间
 loop .2

 mov eax, PageDirBase0
 mov cr3, eax
 mov eax, cr0
 or eax, 80000000h
 mov cr0, eax
 jmp short .3
.3:
 nop   ;空操作,消耗系统时间

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


; 测试分页机制 --------------------------------------------------------------
PagingDemo:
 mov ax, cs
 mov ds, ax
 mov ax, SelectorFlatRW
 mov es, ax

 push LenFoo
 push OffsetFoo   ;把这三段分别有源地址拷贝到相应的基址段内 cs为当前代码段的段地址
 push ProcFoo  ;从cs:OffsetFoo放到SelectorFlatRW:ProcFoo下面,其中ProcFoo为00401000h 4M+4K
 call MemCpy
 add esp, 12

 push LenBar
 push OffsetBar
 push ProcBar  ;从cs:OffsetBar放到SelectorFlatRW:ProcBar下面,其中ProBar为00501000h 5M+4K
 call MemCpy
 add esp, 12

 push LenPagingDemoAll
 push OffsetPagingDemoProc
 push ProcPagingDemo ;从cs:OffsetBar放到SelectorFlatRW:ProcPagingDemo下面,其中ProcPagingDemo为00301000h 3M+4K
 call MemCpy
 add esp, 12

 mov ax, SelectorData
 mov ds, ax   ; 数据段选择子
 mov es, ax

 call SetupPaging  ; 启动分页

 call SelectorFlatC:ProcPagingDemo 
 ;调用SelectorFlatC:ProcPagingDemo,即ProcPagingDemo 00301000h,调用这个函数,该函数调用了一个线性地址
 call PSwitch   ; 切换页目录,改变地址映射关系
 call SelectorFlatC:ProcPagingDemo

 ret
; ---------------------------------------------------------------------------

; 切换页表 ------------------------------------------------------------------
PSwitch:
 ; 初始化页目录
 mov ax, SelectorFlatRW
 mov es, ax
 mov edi, PageDirBase1 ; 此段首地址为 PageDirBase1
 xor eax, eax
 mov eax, PageTblBase1 | PG_P  | PG_USU | PG_RWW
 mov ecx, [PageTableNumber]
.1:
 stosd
 add eax, 4096  ; 为了简化, 所有页表在内存中是连续的.
 loop .1

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

 ; 在此假设内存是大于 8M 的
 mov eax, LinearAddrDemo ;LinearAddDemo 4M4K
 shr eax, 22    ;除以4M,剩1,eax中保存的为dir的第几项
 mov ebx, 4096   ;将dir第几项,转为tbl中的偏移地址
 mul ebx     ;参考图3-26的分页机制示意图,该图将线性地址转换为物理地址
 mov ecx, eax
 mov eax, LinearAddrDemo
 shr eax, 12    ;除以4K
 and eax, 03FFh ; 1111111111b (10 bits)
 mov ebx, 4
 mul ebx
 add eax, ecx
 add eax, PageTblBase1
 mov dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW
;找到哪个是放Foo的线性地址,将表中的值修改,指向 ProBar
 mov eax, PageDirBase1
 mov cr3, eax
 jmp short .3
.3:
 nop

 ret
; ---------------------------------------------------------------------------

; PagingDemoProc ------------------------------------------------------------
PagingDemoProc: ;调用线形地址的命令
OffsetPagingDemoProc equ PagingDemoProc - $$
 mov eax, LinearAddrDemo
 call eax
 retf
; ---------------------------------------------------------------------------
LenPagingDemoAll equ $ - PagingDemoProc
; ---------------------------------------------------------------------------

; foo -----------------------------------------------------------------------
foo:
OffsetFoo equ foo - $$
 mov ah, 0Ch   ; 0000: 黑底    1100: 红字
 mov al, 'F'
 mov [gs:((80 * 17 + 0) * 2)], ax ; 屏幕第 17 行, 第 0 列。
 mov al, 'o'
 mov [gs:((80 * 17 + 1) * 2)], ax ; 屏幕第 17 行, 第 1 列。
 mov [gs:((80 * 17 + 2) * 2)], ax ; 屏幕第 17 行, 第 2 列。
 ret
LenFoo equ $ - foo
; ---------------------------------------------------------------------------

; bar -----------------------------------------------------------------------
bar:
OffsetBar equ bar - $$
 mov ah, 0Ch   ; 0000: 黑底    1100: 红字
 mov al, 'B'
 mov [gs:((80 * 18 + 0) * 2)], ax ; 屏幕第 18 行, 第 0 列。
 mov al, 'a'
 mov [gs:((80 * 18 + 1) * 2)], ax ; 屏幕第 18 行, 第 1 列。
 mov al, 'r'
 mov [gs:((80 * 18 + 2) * 2)], ax ; 屏幕第 18 行, 第 2 列。
 ret
LenBar equ $ - bar
; ---------------------------------------------------------------------------

; 显示内存信息 --------------------------------------------------------------
DispMemSize:
 push esi
 push edi
 push ecx

 mov esi, MemChkBuf
 mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构
.loop:     ;{
 mov edx, 5   ; for(int j=0;j<5;j++) // 每次得到一个ARDS中的成员,共5个成员
 mov edi, ARDStruct  ; {   // 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
.1:     ;
 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) // AddressRangeMemory : 1, AddressRangeReserved : 2
 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
; END of [SECTION .s32]

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[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 al, 11111110b
 mov cr0, eax

LABEL_GO_BACK_TO_REAL:
 jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

抱歉!评论已关闭.