zoukankan      html  css  js  c++  java
  • 操作系统开发系列—2.进入32位保护模式

    源码如下:

    ; ==========================================
    ; pm.asm
    ; 编译方法:nasm pm.asm -o pm.bin
    ; ==========================================
    
    %include	"pm.inc"	; 常量, 宏, 以及一些说明
    
    org	07c00h
    	jmp	LABEL_BEGIN
    
    [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]
    
    [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
    
    	; 为加载 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]
    
    	; 关中断
    	cli
    
    	; 打开地址线A20
    	in	al, 92h
    	or	al, 00000010b
    	out	92h, al
    
    	; 准备切换到保护模式
    	mov	eax, cr0
    	or	eax, 1
    	mov	cr0, eax
    
    	; 真正进入保护模式
    	jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs,
    					; 并跳转到 SelectorCode32:0  处
    ; END of [SECTION .s16]
    
    
    [SECTION .s32]; 32 位代码段. 由实模式跳入.
    [BITS	32]
    
    LABEL_SEG_CODE32:
    	mov	ax, SelectorVideo
    	mov	gs, ax			; 视频段选择子(目的)
    
    	mov ecx, 28
    	mov	edi, 0
    	mov	bx, BootMessage
    .show:
    	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
    	mov	al, [bx]
    	mov	[gs:edi], ax
    	inc edi
    	inc edi
    	inc bx
    	loop .show
    
    	; 到此停止
    	jmp	$
    
    BootMessage:		db	"Joey, I'm in protected mode!"
    SegCode32Len	equ	$ - LABEL_SEG_CODE32
    ; END of [SECTION .s32]

    运行结果如下:

    源码解析:

    1.首先程序跳转至LABEL_BEGIN处,jmp LABEL_BEGIN。将ds、es、ss段寄存器全部初始化为当前代码段。

    2.初始化32位代码段描述符

    在实模式下,也就是8086的16位的CPU的寻址方式是段x16+偏移,而在保护模式下,段寄存器值变成了一个索引,这个索引指向段描述符,也就是GDT中对应的那个描述符,描述符中包含了最终的基地址。

     38-41行表示将当前程序代码段左移4位也就是x16,再加上LABEL_SEG_CODE32的偏移地址,最终形成LABEL_SEG_CODE32的物理地址,42-45表示把此物理地址加载到段描述符对应的段基址位置,段描述符格式如下图所示

    段基地址0-15位也就是LABEL_DESC_CODE32 + 2地址处,将16位的ax传入,第43行向右移动16位表示已经传入的16位消除,然后将剩余的两个8位高低寄存器值传入对应的段基地址处。

    Descriptor是在pm.inc中定义的宏,如下图所示,表示的就是段描述符的格式:

    3.加载GDTR,GDRT的结构图如下所示

    GDTR是唯一的一个指向段描述符表的寄存器,48-55行就是把段描述符表的基地址加载到GDTR寄存器当中。48行清除eax值,49-52是把LABEL_GDT的物理地址加载到20行的GdtPtr的双字处,也就是GDT基地址。55行把GdtPtr地址的内容加载到GDTR,双字节dw对应的是16位界限,双字dd对应的是32位基地址。

    4.关中断和打开A20

    保护模式下中断处理机制和实模式不同,所以先关闭中断,指令为cli。打开A20为历史原因防止偏移超出最大值时回滚。58-63行

    5.切换到保护模式

    只要把寄存器cr0的第0位置为1就行了。66-68行

    6.真正进入保护模式的代码段

    jmp dword SelectorCode32:0,这一行是真正进入保护模式的代码,SelectorCode32是个段选择子,段选择子的作用是找到对应的段描述符从而找到对应的段基地址。段选择子的格式如下图所示

    从位3开始表示为段描述符表的索引,这句代码执行完之后就会把描述符LABEL_DESC_CODE32中的段基址也就是LABEL_SEG_CODE32加载到cs。

    dword的作用是因为当前还是16位的代码,如果没有dword,SelectorCode32:0的偏移地址如果超出16位那么只会截取16位。

    到此已经完全进入32位保护模式的代码了。

    7.显示字符

    接下来就会跳转到LABEL_SEG_CODE32处运行指令了,80-81行把段选择子SelectorVideo传入段寄存器gs中,也就是描述符LABEL_DESC_VIDEO的段基地址0B8000h,这就是显存的基地址。84行edi是显存的偏移地址,87-88行是字符及字符属性,89行为最终显示字符。

    8.描述符属性

    代码段的属性是DA_C + DA_32,根据pm.inc中的定义,DA_C是98h

    对应的二进制为10011000。根据第2条的格式,DA_C其实是上面的第8-15位,第15位P是1表明这个段在内存中存在,S位是1表明这个段是代码段或数据段,TYPE为1000也就是8表明这个段是只执行的代码段,TYPE格式如下图所示

    DA_32是4000h,

    4000h为100000000000000,表明是从上面的位8开始计算的后面15位也就是D/B位为1,表明这个段是32位的代码段。

    一个码农的日常 

    源码及软盘映像

  • 相关阅读:
    【ROM修改教程】添加高级电源重启菜单(安卓4.0.4官方ROM)
    MTK 快速开机 技术详解
    MT6592 经验积累
    Android系统之Broadcom GPS 移植
    Android关机流程源码分析
    Android 4.1.2系统添加重启功能
    android4.2添加重启菜单项
    MTK平台 Android4.0.3 定制关机动画
    不进化,则消亡——互联网时代企业管理的十项实践
    前端切图:调用百度地图API
  • 原文地址:https://www.cnblogs.com/joey-hua/p/5347257.html
Copyright © 2011-2022 走看看