重新认识intel保护模式

段机制在8086和80386的区别

  • 最直接的区别是寻址方式的不同,8086下计算逻辑地址使用段寄存器左移4位加偏移地址的方法;80386的段寄存器的作用变了,存放的是真正的地址在描述符表中的索引,计算方法变成了描述符表中的段基址加偏移地址的方法
  • 更深层次区别是80386不仅计算逻辑地址的方式不同,而且CPU跳转时引入了权限检查,起到保护的作用
  • 之前的认识误区:386引入段机制,是出于兼容8086的段机制,现在理解386并没有在寻址上兼容8086(如果兼容两者寻址算法应该一样)。所谓“兼容”是寄存器上的沿用,使8086引入的段寄存器不被抛弃,除了把段寄存器作为仍然作寻址用,还利用多出来低3位进行权限检查。使保护模式成为可能。

8086段寄存器寻址方式

  • 一句话解释,cpu利用段寄器左移4bit取值,加上偏移地址算出物理地址
    物理地址=段寄存器*16 + 偏移
  • 实验
    org 07c00h          ; 告诉编译器程序加载到7c00处
    jmp 07c0h:DispStrOff

code:
times   10  db  0   
    ;   never reach here

DispStrOff  equ $   -   $$
DispStr:
    mov ax, BootMessage
    mov bp, ax          ; ES:BP = 串地址
    mov cx, 16          ; CX = 串长度
    mov ax, 01301h      ; AH = 13,  AL = 01h
    mov bx, 000ch       ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
    mov dl, 0
    int 10h         ; 10h 号中断
    jmp $

BootMessage:        db  "Hello, OS world!"
root@hy:/home/work/orange/source/mywork# cat real_mode.dis 
00007C00  EA0F00C007        jmp 0x7c0:0xf	//使用段寄存器的跳转 cs:0x7c0 off=0xf 
00007C05  0000              add [bx+si],al
00007C07  0000              add [bx+si],al
00007C09  0000              add [bx+si],al
00007C0B  0000              add [bx+si],al
00007C0D  0000              add [bx+si],al
00007C0F  B8237C            mov ax,0x7c23
00007C12  89C5              mov bp,ax
00007C14  B91000            mov cx,0x10
00007C17  B80113            mov ax,0x1301
00007C1A  BB0C00            mov bx,0xc
00007C1D  B200              mov dl,0x0
00007C1F  CD10              int 0x10
00007C21  EBFE              jmp short 0x7c21	// 没有使用段寄存器的短跳转,直接使用偏移。不使用CS参与计算。
......

上面一段程序直接写死了cs的值,依然可以成功跳转,因为我们知道cpu计算地址的方法,反汇编代码后得到跳转的目标地址0x7C0F,反推出CS的值。
JMP前,CPU准备执行下面这条指令,此时cs = 0,ip = 0x7C00。当前地址 = 0 * 16 + 0x7C00 = 0x7C00
CS没有内容。
00007C00 EA0F00C007 jmp 0x7c0:0xf

运行结果:
在这里插入图片描述
JMP执行后:当前地址 = 0x7C0 * 16 + 0xF = 0x7C0F,可以看到执行时CS寄存器被加载,IP寄存器被加载。
在这里插入图片描述

80386段寄存器寻址方式(分页未开启情况)

  • 一句话解释,cpu利用段寄存器查找描述符获取物理地址基址,加上偏移算出物理地址
    描述符表定义所有段的物理地址基址,段寄存器提供在表中查找描述符的索引

    物理地址 = GDT[index]取段基址部分 + OFF或者物理地址 = LDT[index]取段基址部分 + OFF

  • 保护模式寻址流程

    • 编写代码段,数据段
    • 计算出段基址和段长度
    • 定义GDT表,填入段基址
    • 定义选择子,方便寻址时载入段寄存器
    • 计算GDT地址,使用lgdt指令加载到GDTR
    • 关中断,打开A20地址线,cr0最低位PE置位
    • jmp指令加载选择子到CS,进入保护模式
  • 实验

    • 准备阶段
%include    "pm.inc"    ; 常量, 宏, 以及一些说明
org 07c00h
    jmp LABEL_BEGIN

;1)准备代码段,计算出段基址和段长度
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS   32]
LABEL_SEG_CODE32:	// 标号就是段基址
    mov ax, SelectorVideo
    mov gs, ax          ; 视频段选择子(目的)

    mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。
    mov ah, 0Ch         ; 0000: 黑底    1100: 红字
    mov al, 'P'
    mov [gs:edi], ax

    ; 到此停止
    jmp $

SegCode32Len    equ $ - LABEL_SEG_CODE32	// 计算段长度
; END of [SECTION .s32]

;2)定义GDT(Global Descriptor Table),先定义一个格式,段基址后面写入
[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基地址
;3)定义选择子
; GDT 选择子
SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT
SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]

;4)填入之前没有写入的段基址
[SECTION .s16]
[BITS   16]
LABEL_BEGIN:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0100h

    ; 初始化 32 位代码段描述符
    xor eax, eax
    mov ax, cs
    shl eax, 4
    add eax, LABEL_SEG_CODE32
    mov word [LABEL_DESC_CODE32 + 2], ax
    shr eax, 16
    mov byte [LABEL_DESC_CODE32 + 4], al
    mov byte [LABEL_DESC_CODE32 + 7], ah
;5)计算GDT地址,加载 GDTR 
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_GDT      ; eax <- gdt 基地址
    mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
    ; 加载 GDTR
    lgdt    [GdtPtr]
;6)关中断,打开地址线,cr0 PE 位置位
    ; 关中断
    cli

    ; 打开地址线A20
    in  al, 92h
    or  al, 00000010b
    out 92h, al

    ; 准备切换到保护模式
    mov eax, cr0
    or  eax, 1
    mov cr0, eax
;7)进入保护模式
    jmp dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs,
; END of [SECTION .s16]

运行结果:
在这里插入图片描述我们在jmp dword SelectorCode32:0前断住,查看当前指令地址0x000000007c8d,段选择子0x0008,cs内容为0。
选择子低三位做其它用,其余部分作为索引,index=8%8 = 1,指示cpu去gdt表的第1条去描述符,查看gdt表第一条基址为base=0x00007c04,得到目标跳转地址0x00007c04+0=0x00007c04

附:段选择子介绍
在这里插入图片描述
上图是intel手册介绍段选择子的图片。
TI:Table Indicator,指示cpu去GDT(TI=0)取描述符,还是去LDT(TI=1)中取描述符。LDT表属于系统段,在GDT表之后加载。
RPL:Request Privilege Level,指示CPU申请权限。

实验完整代码见:my github 实模式与保护模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

享乐主

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

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

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

打赏作者

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

抵扣说明:

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

余额充值