posts - 87,comments - 0,views - 12355

实模式

  • 运行于16位的CPU环境下:
    • 16位的寄存器
    • 16位的数据总线
    • 20位的地址总线,以及1MB的寻址能力(2^20B)
    • 一个地址由段和偏移两部分组成,物理地址=段值x16+偏移(段值和偏移都是16位,段值左移四位最后计算出来的地址才是20位)

保护模式

  • 运行与32位的CPU环境下
    • 32位的寄存器
    • 32位的数据总线
    • 32位的地址总线,以及4GB的寻址能力(2^32B)
    • 一个地址仍旧由段和偏移两部分组成

保护模式与实模式的区别

  • 实模式下的段值可以看做是地址的一部分,段值为xxxxh表示以xxxx0h(物理地址,段值二进制左移4位,16进制左移一位)开始的一段内存
  • 保护模式下的段值仅仅只是一个索引,指向一个数据结构(GDT或LDT)的一个表项(描述符),表项中详细定义了段的起始地址、界限、属性等内容

GDT(Global Descriptor Table,全局描述符表)

GDT中的每一个描述符就定义一个段

  • 描述符分为3类

    • 代码段和数据段描述符
    • 系统段描述符
    • 门描述符
  • 代码段和数据段描述符的结构
    (共8个字节,段基址4个字节,段界限2个字节,属性2个字节)

代码段和数据段描述符

  • 选择子的结构
    (共2个字节,16位)
    若不考虑前3位,那么可以将选择子看成是对应描述符相对于GDT基址的偏移
    选择子

  • 程序如何将对应段与寄存器联系起来呢?

mov ax, Selector      ; Selector表示一个选择子
mov gs, ax            ; gs表示段寄存器
  • 保护模式下的寻址

保护模式下的寻址

让我们解析下这个寻址过程:

  1. 首先,我们在程序中使用的是逻辑地址(段:偏移的形式)
  2. 段寄存器存储的是一个段描述符的选择子,指向GDT或LDT结构数组中的某个表项
  3. 指向的段描述符结合偏移量在定位到线性地址(由它的基址、界限、属性决定)

实模式跳转到保护模式

分为6步

  • 准备GDT
  • 用lgdt加载gdtr
    • GdtPtr是个小的数据结构,共6个字节(前两个字节是GDT的界限,后4个字节是GDT的基地址),把GDT的物理地址填充到这个数据结构中,然后执行指令lgdt [GdtPtr]将GdtPtr指示的6个字节加载到寄存器gdtr中
      gdtr
      gdtr寄存器与GdtPtr的结构完全一样
  • 关中断
    • 执行指令cli
  • 打开A20
    • 执行指令
; 打开地址线A20
in  al, 92h
or  al, 00000010b
out 92h, al
  • 置cr0的PE位(第0个位置)为1
    • 寄存器cr0的PE位为0表示CPU运行于实模式,为1表示保护模式
    • 执行指令
; 准备切换到保护模式
mov eax, cr0
or  eax, 1
mov cr0, eax
  • 跳转,进入保护模式
    • 此时cs寄存器的值还是实模式下的值,将代码段的选择子装入cs寄存器中
    • 执行指令jmp dword selector:0,selector根据自己的选择子替换,dword不可省略

初始化段描述符---程序解析

以下程序以代码段LABEL_DESC_CODE32为例

  • 准备GDT的信息
[SECTION .gdt]
; GDT
;                              段基址,           段界限,           属性
LABEL_GDT:         Descriptor       0,                0,           0      ; 空描述符
LABEL_DESC_CODE32: Descriptor       0, SegCode32Len - 1, DA_C + DA_32     ; 非一致代码段
LABEL_DESC_VIDEO:  Descriptor 0B8000h,           0ffffh,       DA_DRW     ; 显存首地址
; GDT 结束

GdtLen      equ $ - LABEL_GDT   ; GDT长度
GdtPtr      dw  GdtLen - 1      ; GDT界限
            dd  0               ; GDT基地址

; GDT 选择子
SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT
SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]

LABEL_GDT就是GDT的首地址了

  • 初始化GDT:加载段选择子
xor eax, eax                 ; 清空 eax 寄存器
mov ax, cs                   ; 将当前代码段寄存器 cs 的值加载到 ax 寄存器
shl eax, 4                   ; 将 ax 寄存器的值左移4位(乘以16),获得段的线性地址
add eax, LABEL_DESC_CODE32   ; 将 LABEL_DESC_CODE32 的地址加到 eax 中,得到代码段的线性地址
  • 初始化GDT:设置描述符的其他字段
mov word [LABEL_DESC_CODE32 + 2], ax   ; 将 ax 寄存器的低16位存储到 LABEL_DESC_CODE32+2 的位置,这是段选择子
shr eax, 16                            ; 将 eax 寄存器右移16位,获得高16位
mov byte [LABEL_DESC_CODE32 + 4], al   ; 将 al 寄存器的低8位存储到 LABEL_DESC_CODE32+4 的位置,描述符的访问权限字节
mov byte [LABEL_DESC_CODE32 + 7], ah   ; 将 ah 寄存器的高8位存储到 LABEL_DESC_CODE32+7 的位置,描述符的段基址的高字节
  • 加载全局描述符表寄存器(GDTR)
xor eax, eax                  ; 清空 eax 寄存器
mov ax, ds                    ; 将数据段寄存器 ds 的值加载到 ax 寄存器
shl eax, 4                    ; 将 ax 寄存器的值左移4位(乘以16),获得全局描述符表(GDT)的基地址
add eax, LABEL_GDT            ; 将 LABEL_GDT 的地址加到 eax 中,得到完整的 GDT 的线性地址
mov dword [GdtPtr + 2], eax   ; 将 eax 的值存储到 GdtPtr+2 的位置,准备加载 GDTR 寄存器

lgdt    [GdtPtr]              ; 加载 GDTR
posted on   Dylaris  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示