80386保护模式--分页机制,附pmtest8代码详解
来源:互联网 发布:2017网络实名制 编辑:程序博客网 时间:2024/06/05 04:02
一、分段机制
逻辑地址---分段机制---线性地址---分页机制---物理地址,分页机制示意图如图1。
页目录表中的每一项叫页目录项;页表中的每一项叫页表项。
图 1 页映射表结构
页目录表PDE,及页表PTE如图2。
图 2 页目录表或者页表的表格选项
AVL:供软件使用
P:存在属性位,P=1表项有效,P=0表项无效,即是否在内存中。
R/W位指示该表项所指定的页是否可读、写或执行。如R/W=1,对表项所指定页可进行读、写后者执行;如R/W=0,对表项所指定页可读或者执行,但不能对该指定页写。但是R/W位对页的写保护只有处理器处于用户特权级时发挥作用;当处理器处于系统特权级时,R/W位被忽略,也即总可以读、写或者执行。
U/S位指示该表项所指定的页是否似乎用户级页。如U/S=1,表项所指定页是用户级页,可由任何特权级下执行的程序访问;如U/S=0,表项所指定页是系统页,只能由在系统特权级下执行的程序访问。
A=1,表示已访问过对应的物理页,A=0,表示未访问过对应的物理页。
D=1,表示已写过对应的物理页,D=0,表示未写过对应的物理页。
pm.inc增加的几行代码:
PG_P EQU 1 ; 页存在属性位 0001 PG_RWR EQU0; R/W 属性位值, 读/执行 0000 PG_RWW EQU2; R/W 属性位值, 读/写/执行 0010 PG_USS EQU0; U/S 属性位值, 系统级 0000 PG_USU EQU4; U/S 属性位值, 用户级 0100
PDE,PTE具体形象的图形如图3,线性地址到物理地址转换过程如图4:
图3 PDE,PTE具体形象
图4 线性地址到物理地址的转换过程页表DDDDD001改为DDDDD000 由于abc为12位,正好每个PTE对应4KB
每个PDE和PTE都是4个字节,左侧PDE一共4个,占16字节,右侧PTE每个框里面是1024个,所以总共占1024*4*4字节=8KB,所以总共可以指向的内存大小是1024*4*4KB=16MB。此图中CR0的内容为00211000,在形成地址的时候我们只关心高20位,用高20位加上后面12位全0形成基地址,再由页目录索引表或者页表索引找到PDE和PTE。最后高20位补上偏移地址形成物理地址。
二、pmtest8.asm代码详解
; ==========================================; pmtest8.asm; 编译方法:nasm pmtest8.asm -o pmtest8.com; ==========================================%include"pm.inc"; 常量, 宏, 以及一些说明PageDirBase0equ200000h; 页目录开始地址:2MPageTblBase0equ201000h; 页表开始地址:2M + 4K 这样的大小最多能寻址4GBPageDirBase1equ210000h; 页目录开始地址:2M + 64KPageTblBase1equ211000h; 页表开始地址:2M + 64K + 4KLinearAddrDemoequ00401000hProcFooequ00401000hProcBarequ00501000hProcPagingDemoequ00301000horg0100hjmpLABEL_BEGIN[SECTION .gdt]; GDT; 段基址, 段界限, 属性LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR|DA_32|DA_LIMIT_4K; 0~4G LIMIT=limit*4k+0FFFh 左移12位再加上FFF正好是FFFFFFFF,4GLABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW|DA_LIMIT_4K ; 0~4GLABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_CR|DA_32 ; 非一致代码段, 32LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; DataLABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA|DA_32 ; Stack, 32 位LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址; GDT 结束GdtLenequ$ - LABEL_GDT; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址; GDT 选择子SelectorNormalequLABEL_DESC_NORMAL- LABEL_GDTSelectorFlatCequLABEL_DESC_FLAT_C- LABEL_GDTSelectorFlatRWequLABEL_DESC_FLAT_RW- LABEL_GDTSelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorCode16equLABEL_DESC_CODE16- LABEL_GDTSelectorDataequLABEL_DESC_DATA- LABEL_GDTSelectorStackequLABEL_DESC_STACK- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDT; END of [SECTION .gdt][SECTION .data1] ; 数据段ALIGN32[BITS32]LABEL_DATA:; 实模式下使用这些符号; 字符串_szPMMessage:db"In Protect Mode now. ^-^", 0Ah, 0Ah, 0; 进入保护模式后显示此字符串_szMemChkTitle:db"BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0; 进入保护模式后显示此字符串_szRAMSizedb"RAM size:", 0_szReturndb0Ah, 0; 变量_wSPValueInRealModedw0_dwMCRNumber:dd0; Memory Check Result_dwDispPos:dd(80 * 6 + 0) * 2; 屏幕第 6 行, 第 0 列。_dwMemSize:dd0_ARDStruct:; Address Range Descriptor Structure_dwBaseAddrLow:dd0_dwBaseAddrHigh:dd0_dwLengthLow:dd0_dwLengthHigh:dd0_dwType:dd0_PageTableNumberdd0_MemChkBuf:times256db0; 保护模式下使用这些符号szPMMessageequ_szPMMessage- $$ ;相对于首地址的偏移szMemChkTitleequ_szMemChkTitle- $$szRAMSizeequ_szRAMSize- $$szReturnequ_szReturn- $$dwDispPosequ_dwDispPos- $$dwMemSizeequ_dwMemSize- $$dwMCRNumberequ_dwMCRNumber- $$ARDStructequ_ARDStruct- $$dwBaseAddrLowequ_dwBaseAddrLow- $$dwBaseAddrHighequ_dwBaseAddrHigh- $$dwLengthLowequ_dwLengthLow- $$dwLengthHighequ_dwLengthHigh- $$dwTypeequ_dwType- $$MemChkBufequ_MemChkBuf- $$PageTableNumberequ_PageTableNumber- $$DataLenequ$ - LABEL_DATA; END of [SECTION .data1]; 全局堆栈段[SECTION .gs]ALIGN32[BITS32]LABEL_STACK:times 512 db 0TopOfStackequ$ - LABEL_STACK - 1; END of [SECTION .gs][SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hmov[LABEL_GO_BACK_TO_REAL+3], axmov[_wSPValueInRealMode], sp; 得到内存数movebx, 0movdi, _MemChkBuf.loop:moveax, 0E820hmovecx, 20movedx, 0534D4150hint15h ;往es:di中一次存放20个字节jcLABEL_MEM_CHK_FAILadddi, 20incdword [_dwMCRNumber] ;ds:_dwMCRNumber,记录一共有几个20个字节cmpebx, 0jne.loopjmpLABEL_MEM_CHK_OKLABEL_MEM_CHK_FAIL:movdword [_dwMCRNumber], 0LABEL_MEM_CHK_OK:; 初始化 16 位代码段描述符movax, csmovzxeax, axshleax, 4addeax, LABEL_SEG_CODE16movword [LABEL_DESC_CODE16 + 2], axshreax, 16movbyte [LABEL_DESC_CODE16 + 4], almovbyte [LABEL_DESC_CODE16 + 7], ah; 初始化 32 位代码段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE32movword [LABEL_DESC_CODE32 + 2], axshreax, 16movbyte [LABEL_DESC_CODE32 + 4], almovbyte [LABEL_DESC_CODE32 + 7], ah; 初始化数据段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_DATAmovword [LABEL_DESC_DATA + 2], axshreax, 16movbyte [LABEL_DESC_DATA + 4], almovbyte [LABEL_DESC_DATA + 7], ah; 初始化堆栈段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_STACKmovword [LABEL_DESC_STACK + 2], axshreax, 16movbyte [LABEL_DESC_STACK + 4], almovbyte [LABEL_DESC_STACK + 7], ah; 为加载 GDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, LABEL_GDT; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt[GdtPtr]; 关中断cli; 打开地址线A20inal, 92horal, 00000010bout92h, al; 准备切换到保护模式moveax, cr0oreax, 1movcr0, eax; 真正进入保护模式jmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LABEL_REAL_ENTRY:; 从保护模式跳回到实模式就到了这里movax, csmovds, axmoves, axmovss, axmovsp, [_wSPValueInRealMode]inal, 92h; ┓andal, 11111101b; ┣ 关闭 A20 地址线out92h, al; ┛sti; 开中断movax, 4c00h; ┓int21h; ┛回到 DOS; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.[BITS32]LABEL_SEG_CODE32:movax, SelectorDatamovds, ax; 数据段选择子moves, axmovax, SelectorVideomovgs, ax; 视频段选择子movax, SelectorStackmovss, ax; 堆栈段选择子movesp, TopOfStackcallDispMemSize; 获取内存信息callPagingDemo; 演示改变页目录的效果; 到此停止jmpSelectorCode16:0; 显示内存信息 --------------------------------------------------------------DispMemSize:pushesipushedipushecxmovesi, MemChkBuf ;256字节开始位置movecx, [dwMCRNumber];for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构.loop:;{movedx, 5;for(int j=0;j<5;j++)// 每次得到一个ARDS中的成员,共5个成员movedi, ARDStruct;{// 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type.1:;moveax dword [esi]; ds:esi-->eaxstosd;eax-->es:ediARDStruct[j*4] = MemChkBuf[j*4]; EI自动加4addesi, 4;decedx;cmpedx, 0;jnz.1;如果不为0}cmpdword [dwType], 1;if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2jne.2;如果不相等{moveax, [dwBaseAddrLow]; ds:dwBaseAddrLowaddeax, [dwLengthLow];ds:dwLengthLowcmpeax, [dwMemSize];if(BaseAddrLow + LengthLow > MemSize) ds:dwMemSizejb.2;如果小于mov[dwMemSize], eax;MemSize = BaseAddrLow + LengthLow;.2:;}loop.loop;} 从156字节不断取数据放到es:ARDStructpopecxpopedipopesiret; ---------------------------------------------------------------------------; 测试分页机制 --------------------------------------------------------------PagingDemo:movax, csmovds, ax ;因为后面要复制的内容都是相对于32位代码段的偏移movax, SelectorFlatRWmoves, axpushLenFoopushOffsetFoo ;ds:esi-->es:edipushProcFoo ;escallMemCpy ;此段代码为于lib.inc中,由于以后会用C语言写,暂时不考虑addesp, 12pushLenBarpushOffsetBarpushProcBarcallMemCpy;addesp, 12pushLenPagingDemoAllpushOffsetPagingDemoProcpushProcPagingDemocallMemCpyaddesp, 12 ;至此PagingDemoProc放在内存00301000h,foo放在内存的00401000h,bar放在内存的00501000hmovax, SelectorDatamovds, ax; 恢复原来的数据段选择子moves, axcallSetupPaging; 启动分页,相对距离callSelectorFlatC:ProcPagingDemo ;执行PagingDemoProc,00301000h,分页后形成的地址还是00301000hcallPSwitch; 切换页目录,改变地址映射关系callSelectorFlatC:ProcPagingDemo;执行PagingDemoProc,00301000h,分页后形成的地址还是00301000hret; ---------------------------------------------------------------------------; 启动分页机制 --------------------------------------------------------------SetupPaging:; 根据内存大小计算应初始化多少PDE以及多少页表xoredx, edxmoveax, [dwMemSize]movebx, 400000h; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小divebxmovecx, eax; 此时 ecx 为页表的个数,也即 PDE 应该的个数testedx, edxjz.no_remainder ;如果为0incecx; 如果余数不为 0 就需增加一个页表.no_remainder:mov[PageTableNumber], ecx; 暂存页表个数,ds:PageTableNumber; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.; 首先初始化页目录movax, SelectorFlatRWmoves, axmovedi, PageDirBase0; 此段首地址为 PageDirBase0xoreax, eaxmoveax, PageTblBase0 | PG_P | PG_USU | PG_RWW.1:stosd ;eax-->es:ediaddeax, 4096; 为了简化, 所有页表在内存中是连续的.loop.1 ;共循环ecx次; 再初始化所有页表moveax, [PageTableNumber]; 页表个数movebx, 1024; 每个页表 1024 个 PTEmulebxmovecx, eax; PTE个数 = 页表个数 * 1024movedi, PageTblBase0; 此段首地址为 PageTblBase0xoreax, eaxmoveax, PG_P | PG_USU | PG_RWW.2:stosdaddeax, 4096; 每一页指向 4K 的空间loop.2moveax, PageDirBase0movcr3, eax ;修改cr3moveax, cr0oreax, 80000000hmovcr0, eax;启动分页jmpshort .3.3:nopret; 分页机制启动完毕 ----------------------------------------------------------; 切换页表 ------------------------------------------------------------------PSwitch:; 初始化页目录movax, SelectorFlatRWmoves, axmovedi, PageDirBase1; 此段首地址为 PageDirBase1xoreax, eaxmoveax, PageTblBase1 | PG_P | PG_USU | PG_RWWmovecx, [PageTableNumber].1:stosdaddeax, 4096; 为了简化, 所有页表在内存中是连续的.loop.1; 再初始化所有页表moveax, [PageTableNumber]; 页表个数movebx, 1024; 每个页表 1024 个 PTEmulebxmovecx, eax; PTE个数 = 页表个数 * 1024movedi, PageTblBase1; 此段首地址为 PageTblBase1xoreax, eaxmoveax, PG_P | PG_USU | PG_RWW.2:stosdaddeax, 4096; 每一页指向 4K 的空间loop.2; 在此假设内存是大于 8M 的moveax, LinearAddrDemoshreax, 22movebx, 4096mulebxmovecx, eaxmoveax, LinearAddrDemoshreax, 12andeax, 03FFh; 1111111111b (10 bits)movebx, 4mulebxaddeax, ecxaddeax, PageTblBase1 ;此时eax为0x00212004,这个参考图3,你就会明白的movdword [es:eax], ProcBar | PG_P | PG_USU | PG_RWWmoveax, PageDirBase1movcr3, eaxjmpshort .3.3:nopret; ---------------------------------------------------------------------------PagingDemoProc: ;为了算出LenPagingDemoAllOffsetPagingDemoProcequPagingDemoProc - $$ ;相对于当前代码段的偏移moveax, LinearAddrDemo ;00401000hcalleax ;call cs:eaxretf ;因为上面把CS和EIP都压入了栈LenPagingDemoAllequ$ - PagingDemoProcfoo: ;00401000hOffsetFooequfoo - $$ ;相对于当前代码段的偏移movah, 0Ch; 0000: 黑底 1100: 红字moval, 'F'mov[gs:((80 * 17 + 0) * 2)], ax; 屏幕第 17 行, 第 0 列。moval, 'o'mov[gs:((80 * 17 + 1) * 2)], ax; 屏幕第 17 行, 第 1 列。mov[gs:((80 * 17 + 2) * 2)], ax; 屏幕第 17 行, 第 2 列。ret ;因为上面只改变了EIP,calleaxLenFooequ$ - foobar: ;00501000hOffsetBarequbar - $$ ;相对于当前代码段的偏移movah, 0Ch; 0000: 黑底 1100: 红字moval, 'B'mov[gs:((80 * 18 + 0) * 2)], ax; 屏幕第 18 行, 第 0 列。moval, 'a'mov[gs:((80 * 18 + 1) * 2)], ax; 屏幕第 18 行, 第 1 列。moval, 'r'mov[gs:((80 * 18 + 2) * 2)], ax; 屏幕第 18 行, 第 2 列。retLenBarequ$ - bar%include"lib.inc"; 库函数SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32]; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式[SECTION .s16code]ALIGN32[BITS16]LABEL_SEG_CODE16:; 跳回实模式:movax, SelectorNormalmovds, axmoves, axmovfs, axmovgs, axmovss, axmoveax, cr0and eax, 7FFFFFFEh ; PE=0, PG=0movcr0, eaxLABEL_GO_BACK_TO_REAL:jmp0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置成正确的值Code16Lenequ$ - LABEL_SEG_CODE16; END of [SECTION .s16code]
- 80386保护模式--分页机制,附pmtest8代码详解
- 80386保护模式--中断机制,附pmtest9代码详解
- 80386保护模式--描述符,附pm.inc代码详解
- 80386保护模式下的分页机制
- 深入详解保护模式下的内存分页机制
- 深入详解保护模式下的内存分页机制
- 深入详解保护模式下的内存分页机制
- 80386保护模式--GDT,LDT,TSS,调用门,特权级转移,附pmtest5代码详解
- 《自己动手写操作系统》之分页机制源码错误(pmtest6/pmtest7/pmtest8)
- 保护模式及其编程——分页机制
- 保护模式总结(四)——分页机制
- 保护模式总结(四)——分页机制
- 保护模式及其编程——分页机制
- 自己动手写操作系统80x86保护模式(4)--分页机制
- 保护模式及其编程——分页机制
- 3.保护模式8-页式存储(启动分页机制)
- 在保护模式下启动分页机制,并且使…
- x86分页机制——《x86汇编语言:从实模式到保护模式》读书笔记42
- 从今天起记录每一个问题
- windows7 隐藏文件怎样查看
- linux内核wait_queue深入分析
- 【第八章】 对ORM的支持 之 8.2 集成Hibernate3 ——跟我学spring3
- 老程序员的忠告:不要做浮躁的软件工程师
- 80386保护模式--分页机制,附pmtest8代码详解
- java基础之IO操作
- 幸福家庭的管理秘诀
- 代理服务器概述及工作原理
- 大学生生活消费综合解决方案项目商业计划书
- Comet:基于 HTTP 长连接的“服务器推”技术
- android数据持久化-SQLite数据库(SQLiteOpenHelper 例子)
- Ubuntu命令行修改网络配置方法
- 【第八章】 对ORM的支持 之 8.3 集成iBATIS ——跟我学spring3