This code is used to jump from high-privilege level 0 to low-privilege level 3, which is implemented by the ret instruction. Before the ret instruction is executed, the target code cs eip ss and esp should be prepared in the stack.
This is implemented by the push instruction
;======================================
;pmtest5.asm
;编译方法:nasm pmtest5.asm -o pmtest5.bin
;======================================
%include "pm.inc"; 常量,宏 以及一些说明
;org 07c00h
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT 段基址 段界限 属性
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; NORMAL描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32;
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C;
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW; Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32;
LABEL_DESC_VIDEO : Descriptor 0B8000h, 0ffffh, DA_DRW+DA_DPL3; 显存首地址
LABEL_DESC_LDT : Descriptor 0, LDTLen - 1, DA_LDT; LDT
LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1, DA_C + DA_32
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
LABEL_DESC_STACK3: Descriptor 0, TopOfStack3, DA_DRWA+DA_32+DA_DPL3
;门 入口 门描述符只描述一个入口 代码的段还是由GDT定义
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate+DA_DPL0
GdtLen equ $-LABEL_GDT ;GDT长度
GdtPtr dw GdtLen - 1 ;GDT 界限
dd 0 ; GDT基地址
;GdtPtr也是一个数据结构 前2字节是GDT界限 后4字节是GDT基地址
;GDT 选择子
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT
SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT
SelectorCodeRing3 equ LABEL_DESC_CODE_RING3- LABEL_GDT + SA_RPL3
SelectorStack3 equ LABEL_DESC_STACK3- LABEL_GDT + SA_RPL3
; END of [SECTION .gdt]
[SECTION .data1] ;数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0; 字符串
PMMessage: db "In Protect Mode now.^_^", 0; 在保护模式中显示
OffsetPMMessage equ PMMessage - $$
StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0;
OffsetStrTest equ StrTest - $$
DataLen equ $-LABEL_DATA
;end of[section.data1]
;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $-LABEL_STACK-1
;end of [section.gs]
; 堆栈段ring3
[SECTION .s3]
ALIGN 32
[BITS 32]
LABEL_STACK3:
times 512 db 0
TopOfStack3 equ $ - LABEL_STACK3 - 1
; END of [SECTION .s3]
;这是一个16位代码段 这个程序修改了gdt中的一些值 然后执行跳转到第三个section
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
mov [LABEL_GO_BACK_TO_REAL+3], ax
mov [SPValueInRealMode],sp
;初始化16位的代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE16
mov word [LABEL_DESC_CODE16 +2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16 +4], al
mov byte [LABEL_DESC_CODE16 +7], ah
;初始化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
; 初始化数据段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_DATA
mov word [LABEL_DESC_DATA + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DATA + 4], al
mov byte [LABEL_DESC_DATA + 7], ah
; 初始化堆栈段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK
mov word [LABEL_DESC_STACK + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK + 4], al
mov byte [LABEL_DESC_STACK + 7], ah
;初始化LDT在GDT中的描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_LDT
mov word [LABEL_DESC_LDT + 2], ax
shr eax, 16
mov byte [LABEL_DESC_LDT + 4], al
mov byte [LABEL_DESC_LDT + 7], ah
; 初始化 LDT 中的描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_CODE_A
mov word [LABEL_LDT_DESC_CODEA + 2], ax
shr eax, 16
mov byte [LABEL_LDT_DESC_CODEA + 4], al
mov byte [LABEL_LDT_DESC_CODEA + 7], ah
;初始化 测试调用门的代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE_DEST
mov word [LABEL_DESC_CODE_DEST + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE_DEST + 4], al
mov byte [LABEL_DESC_CODE_DEST + 7], ah
; 初始化堆栈段描述符(Ring3)
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK3
mov word [LABEL_DESC_STACK3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK3 + 4], al
mov byte [LABEL_DESC_STACK3 + 7], ah
; 初始化Ring3描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_CODE_RING3
mov word [LABEL_DESC_CODE_RING3 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE_RING3 + 4], al
mov byte [LABEL_DESC_CODE_RING3 + 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY: ;从保护模式跳回到实模式就到了这里
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, [SPValueInRealMode] ;指针调到了堆栈中 返回到实模式
in al, 92h
and al, 11111101b; 关闭A20地址线
out 92h, al
sti; 开中断
mov ax, 4c00h;
int 21h; 回到dos
;end of section .s16
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorData
mov ds, ax; 数据段选择子
mov ax, SelectorVideo; 视频段选择子
mov gs, ax
mov ax, SelectorStack
mov ss, ax; 堆栈段选择子
mov esp, TopOfStack
;下面显示一个字符串
mov ah, 0Ch; 0000黑底 1100红字
xor esi, esi
xor edi, edi
mov esi, OffsetPMMessage; 源数据偏移
mov edi, (80*10 + 10) * 2; 目的数据偏移 屏幕第10行第0列
cld ;告诉字符串向前移动
.1:
lodsb ;将字符串中的si指针所指向的一个字节装入al中
test al, al ;判断al是否为空 为空跳转到2
jz .2
mov [gs:edi], ax ;不为空显示当前字符
add edi, 2 ;edi加二
jmp .1
.2: ;当第一条字符串显示完毕
call DispReturn ;回车 换行
push SelectorStack3 ;将ss 新堆栈段push
push TopOfStack3 ;将esp 新堆栈指针push
push SelectorCodeRing3 ;将cs 新的代码段 push
push 0 ;将eip push 即在新的代码段的最开始执行程序
retf ;将堆栈中的四个数据pop到相应的寄存器中 完成跳转
call SelectorCallGateTest:0 ;测试调用门
; Load ldt
mov ax, SelectorLDT
lldt ax
jmp SelectorLDTCodeA:0 ; 跳入局部任务
;-------------------------------------------------------------------------
DispReturn:
push eax
push ebx
mov eax, edi
mov bl, 160
div bl
and eax, 0FFh
inc eax
mov bl, 160
mul bl
mov edi, eax
pop ebx
pop eax
ret
;disreturn 结束-------------------------------------------
SegCode32Len equ $-LABEL_SEG_CODE32
;end of section .s32
[SECTION .sdest]; 调用门目标段
[BITS 32]
LABEL_SEG_CODE_DEST:
;jmp $
mov ax, SelectorVideo
mov gs, ax
mov edi, (80*11+0)*2
mov ah, 0Ch
mov al, 'G'
mov [gs:edi], ax
retf
SegCodeDestLen equ $-LABEL_SEG_CODE_DEST
;16位的代码段 由32位代码段跳入 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
;跳回实模式
mov ax, SelectorNormal
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
and al, 11111110b
mov cr0, eax
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置为正确的值
Code16Len equ $ - LABEL_SEG_CODE16
;end of section .s16code
;LDT
[SECTION .ldt]
ALIGN 32
LABEL_LDT:
; 段基址 段界限 属性
LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32
LDTLen equ $ - LABEL_LDT
;ldt 选择子
SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL
; end of section .ldt
;codeA (ldt 32位代码段)
[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:
mov ax, SelectorVideo
mov gs, ax
mov edi, (80 * 12 + 0) * 2 ;
mov ah, 0Ch
mov al, 'Z'
mov [gs:edi], ax
jmp SelectorCode16:0
CodeALen equ $ - LABEL_CODE_A
;end of section .la
; CodeRing3
[SECTION .ring3]
ALIGN 32
[BITS 32]
LABEL_CODE_RING3:
mov ax, SelectorVideo
mov gs, ax
mov edi, (80 * 14 + 0) * 2
mov ah, 0Ch
mov al, '3'
mov [gs:edi], ax
jmp $
SegCodeRing3Len equ $ - LABEL_CODE_RING3
; END of [SECTION .ring3]