认识保护模式
来源:互联网 发布:机房的网络结构示意图 编辑:程序博客网 时间:2024/05/16 08:50
想看懂linux内核必须要理解保护模式,也许它并不复杂只是书上讲的太抽象了,下面让我们来认识一下保护模式,在这里我们只考虑全局描述符表GDT。
先来看下面两个重要的寄存器
CS:代码段寄存器(Code Segment Register),其值为代码段的段值
IP:指令指针寄存器,它用来存放代码段中的偏移地址。在程序运行过程中,它始终指向下一条指令的首地址,它与代码段寄存器CS联用确定下一条指令的物理地址
CPU要运行就要执行代码,执行那的代码呢?
CPU永远将CS:IP指向的内存单元中的内容看做要执行的下一条指令。
下面这一点其实也很重要:
jmp的实质就是修改CS和IP这两个寄存器
实模式下和保护模式最大的差别就是地址转换方式发生了变化(当然还有其他)
实模式下 :地址值=段值:偏移值=段值*0x10+偏移值((段值<<4)+偏移值)
保护模式下:地址值=选择子:偏移值=描述符表中第(选择子>>3)项描述符给出的段基址+偏移值
实模式下,假设在CS中存放的是0x8,IP中存入0xFFFF,
那么CS:IP=0x8*0x10+0xFFFF=0x1007F,程序下一步要执行的指令的地址就是0x1007F
这就是众所周知的“段地址左移4位加偏移”(CS<<4+IP)
保护模式下,假设CS=0x8,IP=0xFFFF
那么CS:IP=全局描述符表中第1(0x8>>3)项描述符给出的段基址+0xFFFF
其实CS中的值右移3位可理解为数组的下标,这个数组的首地址就存放在lgdtr寄存器中,这个数组就是全局描述符表GDT,数组中的数组项是一个叫做描述符的结构体
要用保护模式我们应该怎么做呢?我们用C语言来描述着个过程吧
1.定义一个描述符结构体Descriptor
typedef struct{
unsigned int lim_low,//段界限低16位(0-15).word lim&0xffff;
unsigned int base_low,//段基址低16位(0-15).word base&0xffff;
char base_mid.byte, //段基址中间8位(16-23)(base>>16)&0xff;
unsigned int type,//段界限高4位(16-19)与属性的组合.word ((lim>>8)&0xf00)|(type&0x0f0ff);
char base_high//段基址高8位(24-31).byte ((base>>24)&0xff)
}Descriptor;
2.设置区全局描述符表gdt
Descriptor gdt[3];
gdt[0] = Descriptor_DUMMY;//第0项不用里面全是0
gdt[1] = Descriptor_CODE32;//里面存放32位代码段的段基址和段界限
gdt[2] = Descriptor_VIDEO;//里面存放显存的段基址和段界限
3.将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器,即lgdt命令
gdtr=((gdt<<16)|(gdt+sizeof(gdt));//
4.关中断,打开地址线A20,设置cr0寄存器
5.跳转,让CS:IP指向你要执行的代码即可
CS=0x8//取gdt表中的第1项,里面有段基址等信息,gdt表在那里?问gdtr寄存器
IP=0xff //偏移地址
大致流程是这样的,下面看一下AT&T汇编代码吧:
.include "pm.inc".text.globl start.code16start: jmpl $0x0, $code /**----------------------------------------------------------------- * Global Descriptor Table: GDT *-------------------------------*/GDT_START:Descriptor_DUMMY: Descriptor 0, 0, 0Descriptor_CODE32: Descriptor 0, 0xFFFFF, (DA_C | DA_32) Descriptor_VIDEO: Descriptor 0xB8000, 0xFFFF, DA_DRW .set GdtLen,(. - GDT_START) /* GDT Lenght */GdtPtr: .2byte GdtLen /* GDT Limit */ .4byte GDT_START /* GDT Base */msg: .string "Hello world!"code: mov %cs,%ax mov %ax,%ds mov %ax,%es mov %ax,%ss mov $0x8000,%sp /*显示HelloWorld字符串*/ mov $msg ,%ax mov %ax ,%bp mov $12 ,%cx mov $0x1301,%ax mov $0x000c,%bx mov $0 ,%dl int $0x10/*加载gdtr即将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器*/ lgdt GdtPtr/*关中断*/ cli/*打开地址线A20*/ inb $0x92,%al or $0x02,%al outb %al,$0x92/*设置cr0寄存器,切换到保护模式*/ movl %cr0,%eax or $1,%eax movl %eax,%cr0/*真正进入保护模式,执行此命令后CS=0x8,IP=LABEL_SEG_CODE32的偏移地址*/ ljmp $0x8,$(LABEL_SEG_CODE32)/*此时CS:IP=全局描述符表中第1(0x8>>3)项描述符给出的段基址+LABEL_SEG_CODE32的偏移地址*/LABEL_SEG_CODE32:.align 32.code32 movw $0x10,%ax movw %ax,%gs movl $((80*11+79)*2),%edi/*第11行,79列*/ movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/ movb $'P',%al/*显示的字符*/ movw %ax,%gs:(%edi)loop2: jmp loop2.org 0x1fe, 0x90 .word 0xaa55
pm.inc中的代码:
/**----------------------------------------------------------------- * 描述符数据结构 *-------------------------------------------------------------*/.macro Descriptor Base, Limit, Attr .2byte \Limit & 0xFFFF .2byte \Base & 0xFFFF .byte (\Base >> 16) & 0xFF .2byte ((\Limit >> 8) & 0xF00) | (\Attr & 0xF0FF) .byte (\Base >> 24) & 0xFF.endm/**----------------------------------------------------------------- * 描述符属性 *----------------------------*/.set DA_32, 0x4000 /* 32-bit segment */.set DA_DRW, 0x92 /* Read/write */.set DA_C, 0x98 /* Execute-only */
如果上面pm.inc中的代码不好理解也可以写成这样:
#define Descriptor(base,lim,type)\.word lim&0xffff;\.word base&0xffff;\.byte (base>>16)&0xff;\.word ((lim>>8)&0xf00)|(type&0x0f0ff);\.byte ((base>>24)&0xff)DA_C = 0x98DA_32 = 0x4000DA_DRW = 0x92.text.globl start.code16start: jmpl $0x0, $code GDT_START:Descriptor_DUMMY:Descriptor(0x0,0x0,0x0)Descript_CODE32 :Descriptor(0x0,0xffffffff,DA_C+DA_32)Descriptor_VIDEO:Descriptor(0xb8000,0x0ffff,DA_DRW)GDT_END:GdtPtr:.word (GDT_END-GDT_START)-1# so does gdt .long GDT_START # This will be rewrite by code.msg: .string "Hello world!"code:mov %cs,%ax mov %ax,%ds mov %ax,%es mov %ax,%ss mov $0x8000,%sp /*显示HelloWorld字符串*/mov $msg ,%axmov %ax ,%bpmov $12 ,%cxmov $0x1301,%axmov $0x000c,%bxmov $0 ,%dlint $0x10/*加载gdtr即将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器*/ lgdt GdtPtr/*关中断*/cli/*打开地址线A20*/inb $0x92,%alor $0x02,%aloutb %al,$0x92/*设置cr0寄存器,切换到保护模式*/movl %cr0,%eaxor $1,%eaxmovl %eax,%cr0/*真正进入保护模式,执行此命令后CS=0x8,IP=LABEL_SEG_CODE32的偏移地址*/ljmp $0x8,$(LABEL_SEG_CODE32)/*此时CS:IP=全局描述符表中第1(0x8>>3)项描述符给出的段基址+LABEL_SEG_CODE32的偏移地址*/LABEL_SEG_CODE32:.align 32.code32movw $0x10,%axmovw %ax,%gsmovl $((80*11+79)*2),%edi/*第11行,79列*/movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/movb $'P',%al/*显示的字符*/movw %ax,%gs:(%edi)loop2:jmp loop2.org 0x1fe, 0x90 .word 0xaa55
其实你也可以在jmp前面添加如下代码:
/*初始描述符Descript_CODE32*/xor %eax, %eaxmov %cs, %ax # %cs -> % ax shl $4, %eax # 左移四位 addl $(LABEL_SEG_CODE32), %eax # 下面是用LABEL_SEG_CODE32填充描述符Descript_CODE32movw %ax, (Descript_CODE32 + 2)shr $16, %eaxmovb %al, (Descript_CODE32 + 4)movb %ah, (Descript_CODE32 + 7)
然后这样跳转:
ljmp $0x8,$0
我们可以讲初始化描述符定义成一个宏:
.macro InitDescrptor Descriptor, SegBase xor %eax, %eax mov %cs, %ax /* %cs -> % ax */ shl $4, %eax /*左移四位 */ addl $(\SegBase), %eax /* 下面是用SegBase填充描述符Descriptor */ movw %ax, (\Descriptor + 2) shr $16, %eax movb %al, (\Descriptor + 4) movb %ah, (\Descriptor + 7).endm
当然也可以这样:
/**InitDescrptor(Descriptor,SegBase)初始化描述符函数*Descriptor:要初始化的描述符*SegBase:段基址*/#define InitDescrptor(Descriptor,SegBase)\xor %eax,%eax; \mov %cs,%ax ; \ shl $4,%eax ; \ addl $(SegBase), %eax ;\ movw %ax, (Descriptor + 2);\shr $16, %eax;\movb %al, (Descriptor + 4);\movb %ah, (Descriptor + 7)
这样我们就可以像C语言一样使用了
/*初始描述符Descript_CODE32*/InitDescrptor(Descript_CODE32,LABEL_SEG_CODE32);
还记得吗jmp的实质就是改变CS和IP寄存器的值,我们只要保证CS:IP=我们的目的地,然后你就会看到跳转成功。
上面代码含有宏,因as的预处理能力有限,所以要先用gcc做预处理可以先gcc -E myboot.S > myboot.s或gcc -E myboot.S -o myboot.s再进行编译
也可用下面的Makefile进行编译:
# Makefile for the simple example kernel.AS =asLD =ldLDFLAGS_CODE32 =-m elf_i386 -e startup_32 -Ttext 0LDFLAGS_CODE16 = --oformat binary -N -e start -Ttext 0x7c00Image:myboot #headdd bs=512 if=myboot of=Image count=1 conv=notruncsyncmyboot:myboot.s$(AS) -o myboot.o -a myboot.s$(LD) $(LDFLAGS_CODE16) -o myboot myboot.oclean:rm -f Image myboot.s myboot head *.o
贴出nasm的汇编代码以供参考:
DA_32EQU4000h; 32 位段DA_DRWEQU92h; 存在的可读写数据段属性值DA_CEQU98h; 存在的只执行代码段属性值%macro Descriptor 3dw%2 & 0FFFFh; 段界限1dw%1 & 0FFFFh; 段基址1db(%1 >> 16) & 0FFh; 段基址2dw((%2 >> 8) & 0F00h) | (%3 & 0F0FFh); 属性1 + 段界限2 + 属性2db(%1 >> 24) & 0FFh; 段基址3%endmacro ; 共 8 字节org07c00hjmpLABEL_BEGIN[SECTION .gdt]; GDT; 段基址, 段界限 , 属性GDT_START:Descriptor_DUMMY: Descriptor 0, 0, 0 ; 空描述符Descript_CODE32: Descriptor 0, 0xffffffff, DA_C + DA_32; 非一致代码段Descriptor_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址; GDT 结束GdtLenequ$ - GDT_START; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址; END of [SECTION .gdt][SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hmov 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 号中断 ; 为加载 GDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, GDT_START; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt[GdtPtr]; 关中断cli; 打开地址线A20inal, 92horal, 00000010bout92h, al; 准备切换到保护模式moveax, cr0oreax, 1movcr0, eax; 真正进入保护模式jmpdword 8:LABEL_SEG_CODE32; 执行这一句会把 SelectorCode32 装入 cs,; 并跳转到 Code32Selector:0 处BootMessage: db "Hello, OS world!" ; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.[BITS32]LABEL_SEG_CODE32:movax, 0x10movgs, ax; 视频段选择子(目的)movedi, (80 * 11 + 79) * 2; 屏幕第 11 行, 第 79 列。movah, 0Ch; 0000: 黑底 1100: 红字moval, 'P'mov[gs:edi], ax; 到此停止jmp$SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32]
- 认识保护模式
- 3.保护模式2----认识保护模式B
- 认识保护模式之汇编复习1
- 认识保护模式之汇编复习2
- 认识保护模式之汇编复习3
- 学习笔记--实践认识保护模式
- 《Orange’s 一个操作系统的实现》3.保护模式2----认识保护模式A
- 《Orange’s 一个操作系统的实现》3.保护模式2----认识保护模式B
- 学习笔记:一个操作系统的实现--认识保护模式
- 《一个操作系统的实现》(三):1.认识保护模式
- 一个操作系统的实现(2)-认识保护模式
- 动手制作操作系统——认识实模式与保护模式
- 保护模式
- 保护模式
- 保护模式
- 保护模式
- 保护模式
- 保护模式
- Android 开源3D游戏引擎汇总
- blastall的使用方法,以及输出文件的格式记录
- 类jQuery selector的控件查询iQuery开源类库介绍
- GPIO控制一个外部接口驱动
- Ubuntu 修改myeclipse背景色
- 认识保护模式
- UNDERSTANDING HTK ERROR MESSAGES
- 朋友无言
- C#执行oracle返回游标类型的存储过程
- Android自动化测试在多种屏幕下的注意事项
- jquery $(document).ready() 与window.onload的区别
- 图像拼接 opencv
- 关于EditText的焦点问题(默认进入不弹出键盘)
- MyEclipse下XFire开发Webservice实例