美文网首页
[055][x86汇编语言]16.3.2 使用高端1MB线性地址

[055][x86汇编语言]16.3.2 使用高端1MB线性地址

作者: AkuRinbu | 来源:发表于2018-08-02 02:01 被阅读102次

    学习笔记

    《x86汇编语言:从实模式到保护模式》
    https://www.jianshu.com/p/d481cb547e9f

    将内核映射到高端地址

    说明

    • 任务的4GB空间
    任务的4GB包括:局部空间和全局空间
    4G虚拟内存空间的线性地址
    
    
    高端2G 是 0x8000 0000~0xFFFF FFFF 
    用于 全局空间 用于指向内核的页表
    
    低端2G 是 0x0000 0000~0x7FFF FFFF 
    用于 局部空间 用于指向任务的页表
    

    实现

    • 一、需要修改页目录
    在内核的页目录表中,创建一个和 线性地址 0x 8000 0000
    (高端2G空间起始线性地址 )相对应的目录项,并使它指向指定的页表。
    
    对内核程序而言,内核程序的任务也是内核程序自己,
    意味着 属于内核程序的页表 和 内核任务的页表 是同一个页表。
    
    • 二、需要修改与内核有关的段描述符
    需要依次修改有关的段描述符
    #  初始代码段
    #  初始栈段
    #  文本模式显存
    #  公用例程段
    #  内核数据段
    #  内核代码段
    #  GDT
    

    效果

    • 完成上面的实现步骤,可以达到这样的效果:在任何任务内,如果段部件发出的线性地址高于或者等于0x8000 0000,指向和访问的就是全局地址空间,或者说内核

    一、需要修改页目录

    什么叫"页目录也是一个物理页"?

    • 对内存而言,数据是数据,代码也是数据,一切都只是数据;
    • 分页,就是按照4KB(4K Byte = 4096字节 = 0x1000 字节)为单位算作一个页;
    • 1个任务有且只有只需要1个页目录,页目录里是1024个页表的物理地址,1个物理地址需要4字节存储,可知,1个页目录的大小正好就是4KB,那么本质上就是内存中的某个物理页被拿来当做页目录了而已,这是标题的意思;
    • 1个页表,里面是1024个也页的物理地址,那么1个页表的大小也正好是4KB,所以在内存里也有某些物理页,成为了页表;
    • 能不能既是页目录又是页表?可以的。
    1.对不同的任务而言,这完全就是同一块内存被反复覆盖刷新使用而已,
    你把某个物理页当页目录,我把它当页表或者其他,
    再顺理成章不过了,内存就是这样重复用的;
    
    2.同一个任务呢?其实,也是可以的!
    只要在页目录的某个表项填入页目录自己本身的物理地址,
    使得经过由 
    段部件生成线性地址→取高10位做页目录表偏移量→取得页表的物理地址时,
    拿到的页表物理地址是页目录自己的物理地址,
    再继续
    取中10位做页表的偏移地址,
    此时页表就是页目录,页目录就是页表。
    

    举例说明

    回顾:通过 段部件 以及 页部件 的 线性地址 转 物理地址 计算过程

    • 完全理解检测点16.1的计算过程,知道取高10位、取中10位以及取低12位的计算过程,才能理解下面的举例说明

    检测点16.1 参见 https://www.jianshu.com/p/704044463b52

    举例:前提是在页目录的最后一个表项已经填入了页目录自己的物理地址

    填入页目录自己的物理地址过程见
    https://www.jianshu.com/p/4bb9514c4500

    图解过程取自 16.3.2 节


    使用线性地址访问和修改页目录表
    • 对应配书代码包,源码文件:c16_core.asm (第969~973行)
    ;在页目录内创建与线性地址0x80000000对应的目录项
    mov ebx,0xfffff000                 ;页目录自己的线性地址 
    mov esi,0x80000000                 ;映射的起始地址
    shr esi,22                         ;线性地址的高10位是目录索引
    shl esi,2
    mov dword [es:ebx+esi],0x00021003  ;写入目录项(页表的物理地址和属性)
                                       ;目标单元的线性地址为0xFFFFF200
    
    • 0x0800的意义是什么?
    0x00021003 是第一个页表的物理地址
    在图解的最开始就已经被填入到了页目录的第一个表项之中
    
    4G虚拟内存空间的线性地址
    高端2G 是 0x8000 0000~0xFFFF FFFF
    低端2G 是 0x0000 0000~0x7FFF FFFF
    
    
    对于页目录而言
    页目录最大4KB 也就是0x1000
    从页目录表项的偏移量来看,
    最后一个表项就是 0x0FFC~0x0FFF (占用4个字节)
    而第一个表项是 0x0000~0x0003(占用4个字节)
    
    为什么 高端2G和低端2G 切在了页目录偏移量 0x0800处
    其实就是除以2   0x1000 ÷ 2 = 0x0800
    
    代码最终的目的就是,
    在页目录表偏移量0x0800处写上0x0002 1003(第一个页表的物理地址)
    
    
    谁是因?
    最根本的原因在于,高端2G 和 低端2G的五五开,
    由此而来就是偏移量的五五开,0x0000~0x07FF 和 0x0800~0x0FFF
    那么 低端2G 要用 页表,就在偏移量0x0000 处开始填写页表的物理地址
    同时,高端2G 要用 页表,就需要在偏移量0x0800处填写相同的页表物理地址
    
    0x0800由此而来.
    
    • 偏移量0x0800最后和谁结合?
    处理器遵从的规矩是:你访问的是页目录表,
    但却还要通过页目录表进行地址转换之后才能访问。
    
    即,要符合 “从段部件发出线性地址 经过 取高10位 取中10位 
    以及 取最后偏移量,最后经由页部件算出来的物理地址”,
    这个物理地址才是 目标 页目录表偏移量0x0800所在的 物理地址。
    
    其实也就是说,要注意到 0x0800最后是和谁结合?
    恰恰就是 页目录 自己的物理地址!
    
    为什么是这样?
    对于 段部件 发出的线性地址 0xFFFFF200 而言,
    取高10位,取到的是 0x3FF ,乘以4就是 0xFFC ,
    这就是页目录表的最后一个表项;
    取中10位,取到的仍旧是 0x3FF,乘以4还是0xFFC,
    依旧是页目录表的最后一个表项;
    
    段部件发出的线性地址,是我们精心设计的,
    只要是 0x FFFF??? 前20位全是1,
    那么,取高10位 和 取中10位 就一定是一样的值。
    
    加之,前提是在页目录的最后一个表项已经填入了页目录自己的物理地址,
    所以,就能找到 页目录偏移量0x0800所在的物理地址。
    

    说白了

    • 整个通过 段部件 和页部件 从 线性地址 到 物理地址 的过程没有动,动的只是 一开始的线性地址,程序员知道,后面的转换过程,所以在凑那个0x0800,为什么不一开始就给一个偏移量0x0800,是因为高位必须是0xffff,这样才能找到最后一个页目录表项,经过移位恰恰还有个0x200的偏移量,刚好乘以4就能得到0x800不然我估计作者会显式地赋值,因为必须遵从原理,让处理器固件走一趟取高10位,中10位的操作,把自己的地址写在自己身上,本质上取中10位的偏移量还是访问自己了。

    结果

    • 不同的线性地址,可以指向同一个页表!注意是页表!不是页!页的物理地址填写早在映射操作之前就完成了,就不改变了。

    二、需要修改与内核有关的段描述符

    • 修改:与内核有关的段描述符统统要改
    从数学角度上来说就是 全部加上 0x8000 0000
    因为高端2G就是从线性地址 0x8000 0000 开始的
    
    
    ;下面这些将GDT中的段描述符映射到线性地址0x80000000
    
    ;先取出GDT的物理地址 用来访问GDT
    sgdt [pgdt]
    mov ebx,[pgdt+2]
    
    ;依次修改
    ;#  初始代码段
    or dword [es:ebx+0x10+4],0x80000000
    ;#  初始栈段
    or dword [es:ebx+0x18+4],0x80000000
    ;#  文本模式显存
    or dword [es:ebx+0x20+4],0x80000000
    ;#  公用例程段
    or dword [es:ebx+0x28+4],0x80000000
    ;#  内核数据段
    or dword [es:ebx+0x30+4],0x80000000
    ;#  内核代码段
    or dword [es:ebx+0x38+4],0x80000000
    
    ;最后修改GDT本身描述符
    add dword [pgdt+2],0x80000000 ;GDTR也用的是线性地址
    lgdt [pgdt]
    
    

    GDT布局参考 https://www.jianshu.com/p/4a420617c5db

    • 刷新 :使段寄存器生效,通过重新加载一次对应的段描述符,使得段寄存器CS SS DS的高速缓存刷新
             jmp core_code_seg_sel:flush        ;刷新段寄存器CS,启用高端线性地址 
                                                 
       flush:
             mov eax,core_stack_seg_sel
             mov ss,eax
             
             mov eax,core_data_seg_sel
             mov ds,eax
    
    • 结果:旁边的地址都是线性地址
      映射到高端地址后的系统核心布局

    举例说明:段部件发出的线性地址高于或者等于 0x8000 0000

    举例:线性地址 0x80007E00 与 线性地址 0x0000 7E00 如何指向指向同一个页表.png
    • 线性地址0x80007E00 与 线性地址 0x0000 7E00 如何指向指向同一个页表
    • 加上 0x8000 0000的操作,所影响的是只是取高10位计算出来的页目录偏移量,后面从取中10位以及最后低12位偏移量都是不变的;
    • 不要觉得 0x200 * 4 = 0x800 这里乘以4很奇怪,要回想起来页目录表存的是页表的物理地址,一个物理地址是一个双字,占用4个字节,偏移量0x800起始偏移地址,从0x800开始的0x800 0x801 0x802 0x803四个内存空间一起才是表示一个物理地址,所以是要乘以4的,而且这是处理器固件的设计,是焊死在芯片里的设计,不是程序员的编程;

    相关文章

      网友评论

          本文标题:[055][x86汇编语言]16.3.2 使用高端1MB线性地址

          本文链接:https://www.haomeiwen.com/subject/nbglvftx.html