X86系统内存分页机制--将同一个虚拟地址映射到不同的物理地址 并反编译验证

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实验 : 将同一个虚拟地址映射到不同的物理地址

说明:此处所讲的 <将同一个虚拟地址映射到不同的物理地址> 是指 将 同一个虚拟地址 在不同的页表中进行映射,并直接指定映射的物理地址,此处直接指定的物理地址 依然是某个物理页的起始地址,特点式目标物理地址 低12位为0,即目标物理地址是4K的整数倍 说明是某个物理页面(一页4K大小)的起始地址。所以最终的映射方式不变,依然是虚拟地址高10位 作为 子页表在页目录的位置,中间10位是 目标物理页号地址 在子页表中的位置,最后低12位是 页内偏移地址。

%include "inc.asm"

;两个页目录 子页表 起始地址  对应两个不同的任务
;简单的分页构建方法,所有的子页表顺序的连续排列

;页目录0起始地址
PageDirBase0    equ    0x200000
;子页表0起始地址
PageTblBase0    equ    0x201000

;页目录1起始地址
PageDirBase1    equ    0x300000
;子页表1起始地址
PageTblBase1    equ    0x301000

;用处:在不同的页表中 将目标虚地址X 分别映射到 Y Z 两个目标物理地址
;目标虚地址X  4K字节对齐
ObjectAddrX     equ    0x401000
;目标物理地址Y 低12位0 4K的整数倍 说明是某个物理页面的起始地址
TargetAddrY     equ    0x501000
;目标物理地址Z 低12位0 4K的整数倍 说明是某个物理页面的起始地址
TargetAddrZ     equ    0x601000

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]
; GDT definition
;                                       段基址,           段界限,       段属性
GDT_ENTRY         :     Descriptor        0,                0,           0
CODE32_DESC       :     Descriptor        0,        Code32SegLen - 1,    DA_C + DA_32
VIDEO_DESC        :     Descriptor     0xB8000,         0x07FFF,         DA_DRWA + DA_32
DATA32_DESC       :     Descriptor        0,        Data32SegLen - 1,    DA_DRW + DA_32
STACK32_DESC      :     Descriptor        0,         TopOfStack32,       DA_DRW + DA_32
;页目录段0 段描述符  最大4096 可读可写,32位保护模式下的段
PAGE_DIR_DESC0  :     Descriptor    PageDirBase0,       4095,          DA_DRW + DA_32
;子页表段0 段描述符  最大1024(0--1023)个子页表 可读可写,属性表明 段界限的单位是页(DA_LIMIT_4K) 4K字节,32位保护模式下的段
;默认单位是字节 这里显式的指明了单位是 4K字节
PAGE_TBL_DESC0  :     Descriptor    PageTblBase0,       1023,          DA_DRW + DA_LIMIT_4K + DA_32

;页目录段1 段描述符
PAGE_DIR_DESC1  :     Descriptor    PageDirBase1,       4095,          DA_DRW + DA_32
;子页表段1 段描述
PAGE_TBL_DESC1  :     Descriptor    PageTblBase1,       1023,          DA_DRW + DA_LIMIT_4K + DA_32
;平坦内存模型 段描述符 段基地址是0  段界限4G(DA_LIMIT_4K表示单位是4K)  可读可写
FLAT_MODE_RW_DESC :     Descriptor        0,             0xFFFFF,        DA_DRW + DA_LIMIT_4K + DA_32
; GDT end

GdtLen    equ   $ - GDT_ENTRY

GdtPtr:
          dw   GdtLen - 1
          dd   0
          
          
; GDT Selector

Code32Selector      equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector       equ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector      equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector     equ (0x0004 << 3) + SA_TIG + SA_RPL0
;页目录段0 选择子
PageDirSelector0 equ (0x0005 << 3) + SA_TIG + SA_RPL0
;;子页表段0 选择子
PageTblSelector0 equ (0x0006 << 3) + SA_TIG + SA_RPL0
;页目录段1 选择子
PageDirSelector1 equ (0x0007 << 3) + SA_TIG + SA_RPL0
;子页表段1 选择子
PageTblSelector1 equ (0x0008 << 3) + SA_TIG + SA_RPL0
;平坦内存模型选择子
FlatModeRWSelector  equ (0x0009 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]

TopOfStack16    equ 0x7c00

[section .dat]
[bits 32]
DATA32_SEGMENT:
    DTOS               db  "D.T.OS!", 0
    DTOS_LEN           equ $ - DTOS
    DTOS_OFFSET        equ DTOS - $$
    HELLO_WORLD        db  "Hello World!", 0
    HELLO_WORLD_LEN    equ $ - HELLO_WORLD
    HELLO_WORLD_OFFSET equ HELLO_WORLD - $$

Data32SegLen equ $ - DATA32_SEGMENT

[section .s16]
[bits 16]
ENTRY_SEGMENT:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16
    
    ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT
    mov edi, CODE32_DESC
    
    call InitDescItem
    
    mov esi, DATA32_SEGMENT
    mov edi, DATA32_DESC
    
    call InitDescItem
    
    mov esi, STACK32_SEGMENT
    mov edi, STACK32_DESC
    
    call InitDescItem
    
    ; initialize GDT pointer struct
    mov eax, 0
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY
    mov dword [GdtPtr + 2], eax

    ; 1. load GDT
    lgdt [GdtPtr]
    
    ; 2. close interrupt
    cli 
    
    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al
    
    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax
    
    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0


; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:
    push eax

    mov eax, 0
    mov ax, cs
    shl eax, 4
    add eax, esi
    mov word [edi + 2], ax
    shr eax, 16
    mov byte [edi + 4], al
    mov byte [edi + 7], ah
    
    pop eax
    
    ret

    
[section .s32]
[bits 32]
CODE32_SEGMENT:
    mov ax, VideoSelector
    mov gs, ax
    
    mov ax, Stack32Selector
    mov ss, ax
    
    mov eax, TopOfStack32
    mov esp, eax
    
    mov ax, Data32Selector
    mov ds, ax
    
	;加载平坦内存模型选择子 使用平坦内存模型
    mov ax, FlatModeRWSelector
    mov es, ax
	;将 DTOS_OFFSET位置处字符串 ”D.T.OS!“ 拷贝到指定物理内存TargetAddrY中 长度为DTOS_LEN
    mov esi, DTOS_OFFSET ;source
    mov edi, TargetAddrY ;destination
    mov ecx, DTOS_LEN
    ;拷贝
    call MemCpy32
    
	;将 HELLO_WORLD_OFFSET位置处字符串 Hello World!" 拷贝到指定物理内存TargetAddrY中 长度为 HELLO_WORLD_LEN
    mov esi, HELLO_WORLD_OFFSET  ;source
    mov edi, TargetAddrZ ;destination
    mov ecx, HELLO_WORLD_LEN 
    ;拷贝
    call MemCpy32
    
	;初始化 页表0 内存布局
    mov eax, PageDirSelector0 ;页目录段0 选择子
    mov ebx, PageTblSelector0 ;子页表段0 选择子
    mov ecx, PageTblBase0 ;子页表0起始地址
    call InitPageTable
    
	;初始化 页表1 内存布局
    mov eax, PageDirSelector1
    mov ebx, PageTblSelector1
    mov ecx, PageTblBase1
    call InitPageTable
    
	;在页表0中将虚地址映射到目标物理地址
    mov eax, ObjectAddrX   ; 0x401000 目标虚地址
    mov ebx, TargetAddrY   ; 0x501000 对应物理地址
    mov ecx, PageDirBase0  ; 在0号页目录PageDirBase0中进行映射
    call MapAddress
	
    ;在页表1中将虚地址映射到目标物理地址
    mov eax, ObjectAddrX   ; 0x401000
    mov ebx, TargetAddrZ   ; 0x601000
    mov ecx, PageDirBase1
    call MapAddress
    
    ; mov eax, PageDirBase0
    
    ; call SwitchPageTable
    
    ; mov eax, PageDirBase1
    
    ; call SwitchPageTable
    
    jmp $

;功能:在页表中将虚地址映射到目标物理地址(需要工作在平坦内存模型下)
	;直接将物理地址 存储到 虚拟地址所对应的子页表的偏移位置处。此时映射就是直接的物理地址 不再是物理页面的页号
; es  --> flat mode
; eax --> virtual address 虚地址
; ebx --> target  address 目标物理地址
; ecx --> page directory base 页目录,即改写该页目录中某个虚地址所映射到的目标物理地址
MapAddress:
    push edi
    push esi 
    push eax    ; [esp + 8]
    push ebx    ; [esp + 4]
    push ecx    ; [esp] 当前栈顶
    
    ; 1. 取虚地址高 10 位, 计算子页表在页目录中的位置
    mov eax, [esp + 8] ;获取虚地址
    shr eax, 22 ;右移22位 取高10位
    and eax, 1111111111b
    shl eax, 2 ; *4  = 子页表在页目录中的位置
    
    ; 2. 取虚地址中间 10 位, 计算物理地址在子页表中的位置
    mov ebx, [esp + 8] ;获取虚地址
    shr ebx, 12 ;右移21位 取中间10位
    and ebx, 1111111111b
    shl ebx, 2 ; *4 = 物理地址在子页表中的位置
    
    ; 3. 取子页表起始地址
    mov esi, [esp] ;获取页目录起始地址 栈顶
    add esi, eax  ;获取子页表偏移地址(平坦内存模型) = *(页目录地址+子页表偏移地址)
    mov edi, [es:esi] ;获取子页表起始地址(平坦内存模型)
    and edi, 0xFFFFF000 ;低12位清零
    
    ; 4. 将目标地址写入子页表的对应位置
    add edi, ebx ;目标物理地址所在地址 = 子页表中地址 + 物理地址存储偏移位置
    mov ecx, [esp + 4] ;获取 目标物理地址
    and ecx, 0xFFFFF000 ;低12位清零 4k字节对齐
    or  ecx, PG_P | PG_USU | PG_RWW ;目标物理地址加属性
    mov [es:edi], ecx ;将 目标物理地址 存入子页表对应的位置中
    
    pop ecx
    pop ebx
    pop eax
    pop esi
    pop edi
    
    ret

; es     --> flat mode selector
; ds:esi --> source
; es:edi --> destination
; ecx    --> length
MemCpy32:
    push esi
    push edi
    push ecx
    push ax
    
    cmp esi, edi
    
    ja btoe
    
    add esi, ecx
    add edi, ecx
    dec esi
    dec edi
    
    jmp etob
    
btoe:
    cmp ecx, 0
    jz done
    mov al, [ds:esi]
    mov byte [es:edi], al
    inc esi
    inc edi
    dec ecx
    jmp btoe
    
etob: 
    cmp ecx, 0
    jz done
    mov al, [ds:esi]
    mov byte [es:edi], al
    dec esi
    dec edi
    dec ecx
    jmp etob

done:   
    pop ax
    pop ecx
    pop edi
    pop esi
    ret

;初始化页表    
; eax --> page dir base selector  页目录选择子
; ebx --> page table base selector  子页表选择子
; ecx --> page table base 子页表起始地址
InitPageTable:
    push es
    push eax  ; [esp + 12]
    push ebx  ; [esp + 8]
    push ecx  ; [esp + 4]
    push edi  ; [esp] 栈顶
    
	;设置目录项
	;页目录基地址
    mov es, ax ;获取页目录基地址
    mov ecx, 1024    ;  1K sub page tables 有1024个子页表 循环1024次
    mov edi, 0
    mov eax, [esp + 4] ;获取子页表起始地址
	;子页表基地址 + 属性:对应页已经在内存中 是用户级别的页 可读可写
    or  eax, PG_P | PG_USU | PG_RWW
    
    cld
    
stdir:
	;将 eax中的值存储到[es:edi]指向的内存单元中,edi+4(页目录中下一项子页表)
    stosd
	;填充页目录下一项 子页表地址
    add eax, 4096
    loop stdir
	
    ;设置子页表
	;子页表基地址
    mov ax, [esp + 8] ;获取 子页表选择子
    mov es, ax
    mov ecx, 1024 * 1024   ; 1M pages, 1024个子页表*每个子页表有1024个物理页面条目
    mov edi, 0
	;第一个物理页面地址 接上面累加结果,属性:对应页已经在内存中 是用户级别的页 可读可写
    mov eax, PG_P | PG_USU | PG_RWW
    
    cld
    
sttbl:
	;将 eax中的值存储到[es:edi]指向的内存单元中,edi+4(页目录中下一项子页表)
    stosd
	;填充子页表中记录的下一项 物理页面地址信息
    add eax, 4096
    loop sttbl
    
    pop edi
    pop ecx
    pop ebx
    pop eax
    pop es
    
    ret   

; eax --> page directory base
SwitchPageTable:
    push eax
    
    mov eax, cr0
    and eax, 0x7FFFFFFF
    mov cr0, eax
    
    mov eax, [esp]
    mov cr3, eax
    mov eax, cr0
    or  eax, 0x80000000
    mov cr0, eax
    
    pop eax
    
    ret


; ds:ebp    --> string address
; bx        --> attribute
; dx        --> dh : row, dl : col
PrintString:
    push ebp
    push eax
    push edi
    push cx
    push dx
    
print:
    mov cl, [ds:ebp]
    cmp cl, 0
    je end
    mov eax, 80
    mul dh
    add al, dl
    shl eax, 1
    mov edi, eax
    mov ah, bl
    mov al, cl
    mov [gs:edi], ax
    inc ebp
    inc dl
    jmp print

end:
    pop dx
    pop cx
    pop edi
    pop eax
    pop ebp
    
    ret
    
Code32SegLen    equ    $ - CODE32_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:
    times 1024 * 4 db 0
    
Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32  equ Stack32SegLen - 1

验证:

0x401000虚拟地址所对应的各项信息:

100 0000 0001 0000 0000 0000
低12 : 0000 0000 0000  
中10位 : 00 0000 0001 = j ; 所以 子页表地址 =  页目录地址+1*4
高10位 :1 = k  所以目标物理内存页号存储地址是  子页表地址 + 1*4

.
页表0验证

1 先反编译 : ndisasm -b 32 -o 0x9000 loader > loader.txt
2 找到关键代码指令所对应的地址 : 0x919b
0000919B  EBFE              jmp short 0x919b

3 break 0x9132
4 info break
5 c
// x命令读取内存值
//1:代表度一个单位  w:代表4个字节  x:以16进制方式打印结果
  x /1wx  地址
6 首先读取 页目录中目标子页表地址 (页目录0起始地址0x20000 + 子页表地址 偏移4)
 //x /1wx 0x200004
<bochs:4> x /1wx 0x200004
[bochs]:
0x00200004 <bogus+       0>:	0x00202007  //低12位是属性位
<bochs:5> 
7 再根据子页表地址 查看目标物理内存地址  (子页表地址高20位 + 物理内存地址偏移地址4)
//x /1wx 0x00202004
<bochs:5> x /1wx 0x00202004
[bochs]:
0x00202004 <bogus+       0>:	0x00501007 //读取出来的值 正好是我们写进去的映射地址!!!
<bochs:6> 

页表1验证类似…

代码中 初始化页表的 内存结果如图所示:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值