《ORANGE‘s 一个操作系统的实现》--认识保护模式

认识保护模式

为什么需要保护模式

  • Intel 8086是16位CPU,它有着16位的寄存器,16位的数据总线以及20位的地址总线和1MB的寻址能力。
  • 从80386开始CPU进入32位时代,寻址能力达到4GB,无法使用16位寄存器完成寻址

GDT(global descriptor table)

而保护模式下,虽然段值仍然由原来16位的cs、ds等寄存器表示,但此时它仅仅变成了一个索引,这个索引指向一个数据结构的一个表项,表项中详细定义了段的起始地址、界限、属性等内容。这个数据结构,就是GDT.

描述符(Descriptor)

GDT的作用是用来提供段式存储机制,这种机制是通过段寄存器和GDT中的描述符共同提供的。

描述符的结构

image-20240910135918638

  • 段基址:规定线性地址空间中段的开始地址。在保护模式下,段基地址长32位。因为基地址长度与寻址地址的长度相同,所以段基地址可以是 0~4GB 范围内的任意地址,而不像实方式下规定的边界必须被16整除。
  • 段界限:段界限规定段的大小。在保护模式下,段界限决定了偏移量的最大值。对于向下扩展的段,如堆栈段来说, 段界限决定了偏移量的最小值。
[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]

pmtest1.asm中定义了如上的GDT,包括:

  • 三个描述符:空描述符 非一致代码段 显存首地址,并赋予段基址 段界限 属性 的初值
  • GDT长度
  • GDT指针:包括GDT界限和GDT基地址
  • GDT选择子:包括非一致代码段选择子 显存首地址选择子

选择子(selector)

image-20240910141437914

; GDT 选择子
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT

对于上面的代码片段,TiRPL都是0的时候,选择子可以看做是描述符相对于GDT基地址的偏移

段式寻址

理解[SECTION .s16]程序的关键是要明白此时CPU仍然处于实模式,此时最大的寻址范围仍然是1M,因此可以用cs寄存器存储

[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs				;cs为0
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h

这一节程序是用cs中存储的段基址(即LABEL_BEGIN标签在内存中对应的地址)初始化ds es ss寄存器,并初始化sp寄存器

	; 初始化 32 位代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4				  ;段基址(CS)左移四位
	add	eax, LABEL_SEG_CODE32 ;加上偏移地址(LABEL_SEG_CODE32)

这段程序利用如下公式算出LABEL_SEG_CODE32真实的逻辑地址并存储在eax中(此时仍然处于实模式,计算机的最大寻址空间仍然是1M)
逻辑地址 = 段基址 ∗ 16 + 偏移地址 逻辑地址=段基址*16+偏移地址 逻辑地址=段基址16+偏移地址

	;对照图3.4阅读
	mov	word [LABEL_DESC_CODE32 + 2], ax;BYTE2,3
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al;BYTE4
	mov	byte [LABEL_DESC_CODE32 + 7], ah;BYTE7

这段程序则是将eax(LABEL_SEG_CODE32的真实逻辑地址)赋值给BYTE2 BYTE3 BYTE4 BYTE7作为保护模式下描述符LABEL_DESC_CODE32的基地址

	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds					;ds = cs
	shl	eax, 4
	add	eax, LABEL_GDT			; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; GDT基地址([GdtPtr + 2]) <- gdt 基地址

这段程序是将GDT的信息写入GDTPtr指针

首先计算出LABEL_GDT对应的内存地址,然后将GdtPtr对应的基地址赋值为LABEL_GDT的真实的内存地址

	lgdt	[GdtPtr]

这段程序是将GdtPtr加载到gdtr寄存器当中,gdtr寄存器的格式如下图所示

image-20240910151254302

	; 在保护模式下,如果不关终端程序会发生错误
	cli

	; 打开地址线A20,才能访问所有的内存空间
	in	al, 92h
	or	al, 00000010b
	out	92h, al
  • 问:什么是A20?
  • 答:这是一个历史问题。
    • 8086中,“段:偏移”这样的模式能表示的最大内存是FFFF:FFFF,即10FFEFh。可是8086只有20位的地址总线,只能寻址到1MB,那么如果试图访问超过1MB的地址时会怎样呢?实际上系统并不会发生异常,而是回卷(wrap)回去,重新从地址零开始寻址。可是,
    • 到了80286时,真的可以访问到1MB以上的内存了,如果遇到同样的情况,系统不会再回卷寻址,这就造成了向上不兼容.
    • 为了保证百分之百兼容,IBM想出一个办法,使用8042键盘控制器来控制第20个(从零开始数)地址位,这就是A20地址线,如果不被打开,第20个地址位将会总是零。
	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax

	; 真正进入保护模式
	jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs,
								; 并跳转到 Code32Selector:0  处

这段程序首先通过控制cr0寄存器的第0位(PE标志位:1为实模式,0为保护模式)切换到保护模式

然后使用jmp dword SelectorCode32:0跳转到实模式,将SelectorCode32LABEL_SEG_CODE32标志的真实物理地址当做保护模式的段基址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值