保护模式问题(LDT和自定义段基址的段的冲突问题)
本帖最后由 张国祥 于 2013-1-7 21:13 编辑%include "pm.inc"
org 0100h
jmp start
GDT_DESC: Descriptor 0,0,0
NORMAL_DESC: Descriptor 0,0ffffh,DA_DRW
RETURN_16_DESC: Descriptor 0,0ffffh,DA_C
MAIN_DESC: Descriptor 0,mainlen-1,DA_C+DA_32
DISP_DESC: Descriptor 0b8000h,0ffffh,DA_DRW
STK_DESC: Descriptor 0,TopOfStk+1,DA_DRW+DA_32
DATA_DESC: Descriptor 0,datalen-1,DA_DRW
LDT_DESC: Descriptor 0,ldtlen-1,DA_LDT
TEST_DESC: Descriptor 500000h,0ffffh,DA_DRW
;test和ldt两个描述符是不是不能共存
gdtlim equ $-$-1
;注意长度和界限的区别
dd 0
SEL_NORMAL EQU NORMAL_DESC-GDT_DESC
SEL_RETURN_16 EQU RETURN_16_DESC-GDT_DESC
SEL_MAIN EQU MAIN_DESC-GDT_DESC
SEL_DISP EQU DISP_DESC-GDT_DESC
SEL_STK EQU STK_DESC-GDT_DESC
SEL_DATA EQU DATA_DESC-GDT_DESC
SEL_LDT EQU LDT_DESC-GDT_DESC
SEL_TEST EQU TEST_DESC-GDT_DESC
ldt:
L_CODE_DESC: Descriptor 0,lcodelen-1,DA_C+DA_32
ldtlen equ $-$
SEL_L_CODE EQU L_CODE_DESC-ldt+SA_TIL
lcode:
mov edi,160*0+2*40
mov ah,0ah
mov al,'L'
mov ,ax
jmp SEL_RETURN_16:0
lcodelen equ $-$
data:
RMSPValue dw 0
pmmsg: db 'Protect Mode!',0
pmmsgost equ pmmsg-$
datalen equ $-$
stk:
times 512 db 0
TopOfStk equ $-$-2
start:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h
mov ,ax
mov ,sp
;初始化
movzx eax,ax
shl eax,4
add eax,main
mov word ,ax
shr eax,16
mov byte ,al
mov byte ,ah
mov ax,ds
movzx eax,ax
shl eax,4
add eax,data
mov word ,ax
shr eax,16
mov byte ,al
mov byte ,ah
mov ax,ds
movzx eax,ax
shl eax,4
add eax,return16
mov word ,ax
shr eax,16
mov byte ,al
mov byte ,ah
mov ax,ds
movzx eax,ax
shl eax,4
add eax,ldt
mov word ,ax
shr eax,16
mov byte ,al
mov byte ,ah
mov ax,cs
movzx eax,ax
shl eax,4
add eax,lcode
mov word ,ax
shr eax,16
mov byte ,al
mov byte ,ah
mov ax,ss
movzx eax,ax
shl eax,4
add eax,stk
mov word ,ax
shr eax,16
mov byte ,al
mov byte ,ah
mov ax,ds
movzx eax,ax
shl eax,4
add eax,GDT_DESC
mov dword ,eax
lgdt
cli
in al,92h
or al,00000010b
out 92h,al
mov eax,cr0
or al,1
mov cr0,eax
jmp dword SEL_MAIN:0
real:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,
in al, 92h
and al, 11111101b
out 92h, al
sti
mov ax, 4c00h
int 21h
return16:
mov ax,SEL_NORMAL
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov gs,ax
mov eax, cr0
and al, 11111110b
mov cr0, eax
GoToReal:
jmp 0:real
main:
mov ax,SEL_DISP
mov gs,ax
mov ax,SEL_DATA
mov ds,ax
mov ax,SEL_TEST
mov es,ax
mov ax,SEL_STK
mov ss,ax
mov sp,TopOfStk
mov ah,0ah
mov esi,pmmsgost
mov edi,0
cld
.1:
lodsb
cmp al,0
je .2
mov ,ax
add edi,2
jmp .1
.2:
jmp SEL_RETURN_16:0
mov ax,SEL_LDT
lldt ax
call SEL_L_CODE:0
mainlen equ $-$
很诡异,是不是LDT描述符和TEST描述符不能共存,一运行就崩溃
问题的根本原因是这一句:
gdtlimequ $-$-1把这里的“equ”改成“dw”就Okay了。
下面来具体分析一下,特别是奇偶描述符个数导致有时成功/失败的原因。
首先,lgdt指令的操作数需要一个指向6字节内存区域的指针。在你的程序中,是这样做的:
lgdt 遗憾的是,gdtlim是一个常数 $-$$-1,所以,DS:不会在你指定的位置(GDT表的后面)。
那么,它在哪里呢?
注意,这是一个.COM文件,段寄存器DS指向程序段前缀PSP。因此,gdtlim如果不太大的话,它正好指向PSP内部。在你这个例子中,它正好位于一个非常特殊的位置,即,全零数据和非零数据的交汇处。
在这种情况下,如果描述符
q: Descriptor 0,0,0 ;一旦注释此行,就会出问题没有注释掉,那么gdtlim的数值会随着GDT表的尺寸增大而变大,而且正好使那6个字节中的前两个字节(GDT尺寸)为非零。所以,程序能正常工作。但是,不要得意,此时的GDT的界限值并不是正确的,而是非常大(我做过实验),只是因为非零,所以没有导致程序出错。
然而,如果注释掉那一行,那么,那么gdtlim的数值会随着GDT表的尺寸减小而变小,这正好导致那6个字节中的前两个字节(GDT尺寸)为零(因为PSP中那个地方原本就是0)。此时,执行lgdt指令后,GDT的界限值为0,进入保护模式时出现异常。
:dizzy:场外求助啊 是数量问题撒,换下GDT描述符数量即可- - 看 于渊的书呢? wybmagic 发表于 2013-1-9 19:19 static/image/common/back.gif
看 于渊的书呢?
没错,你会吗?:Q这问题太诡异了 :dizzy:啊啊,求解答,或是有什么好资料推荐也好啊- -来个人吧 o70078 发表于 2013-1-8 21:30 static/image/common/back.gif
是数量问题撒,换下GDT描述符数量即可- -
没有任何资料表示描述符数量有限制啊 和描述符的数量与类型没有关系。具体是出现什么问题,描述一下。 本帖最后由 张国祥 于 2013-1-11 23:37 编辑
李忠 发表于 2013-1-11 20:24 static/image/common/back.gif
和描述符的数量与类型没有关系。具体是出现什么问题,描述一下。
经过我的测试,我手动指定一个固定段基址的描述符(比如5MB处)和LDT描述符同时存在时就会引发错误,但有个神奇的地方就在于如果我再加一个空描述符在GDT内,反而不会出错,
如果再加一条,又会出错,也就是说GDT内的描述符个数为奇数时就会出错(包括首个空描述符)。
我留个源码吧,麻烦帮忙看下,谢谢。,zip包含pm.inc和ldt-5m.asm两个文件
总有个出错位置和出错信息吧,告诉我吧。 全是宏、EQU常量,还是COM,有点晕,你告诉我出错位置我好缩小范围。 李忠 发表于 2013-1-12 12:41 static/image/common/back.gif
全是宏、EQU常量,还是COM,有点晕,你告诉我出错位置我好缩小范围。
我没找到好的工具调试com,不过真的没有出错信息,不论是用dosbox还是虚拟机下的dos,都是直接卡住 李忠 发表于 2013-1-12 12:41 static/image/common/back.gif
全是宏、EQU常量,还是COM,有点晕,你告诉我出错位置我好缩小范围。
出错位置就是gdt下的最后一个描述符,注释掉就会出错 问题的原因已经找到了。 等朕整理一下,今晚告诉你哈。 注意上面那幅图,这是在执行jmp dword SEL_MAIN:0指令前,段寄存器(包括GDTR)的内容(未加注释,程序可以正常执行的情况下)。
可以看到,DS=0xc4b0,GDT表的基地址是0xc5b4,GDT界限为0xcd00
这是gdtlim所指向的PSP内存区域映像:
注意,图中,00c5b4是你的程序写入的GDT基地址,cd00是PSP原有的内容,被误用作GDT界限值。
李忠 发表于 2013-1-12 19:38 static/image/common/back.gif
注意上面那幅图,这是在执行指令前,段寄存器(包括GDTR)的内容(未加注释,程序可以正常执行的情况下)。 ...
{:7_157:}谢谢 李老师,了解dw和equ的区别了,equ仅仅是人方便记忆和计算,,并不占用实际空间,导致lgdt实际取址时的错误。 李忠 发表于 2013-1-12 21:42 static/image/common/back.gif
这是gdtlim所指向的PSP内存区域映像:
注意,图中,00c5b4是你的程序写入的GDT基地址,cd00是PSP原有的内 ...
OK,了解,Bochs我还没怎么用过,我现在所学看的都是于渊老师那本书的。似乎其它适合基础较低的书很少。
页:
[1]