《Orange's 一个操作系统的实现》学习笔记--特权级代码段之间的转移(四)

来源:互联网 发布:java多级菜单 编辑:程序博客网 时间:2024/06/07 19:56

一、演示任务内特权级变换的实例 

下面给出一个演示任务内特权级变换的实例。该实例演示在任务内通过调用门从外层特权级变换到内层特权级;也演示通过段间返回指令从内层特权级变换到外层特权级;还演示通过调用门的无特权级变换的转移。实例使用了任务状态段 TSS,这是因为任务内特权级变换时要使用的内层堆栈指针存放在 TSS 中。

1.实现步骤

该实例的实现步骤为:
(1)实方式下初始化;
(2)切换到保护模式;
(3)设置 TR 和 LDTR。由于在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的 TSS 中,所以在进入保护

模式后设置任务状态段寄存器 TR。由于演示任务使用了局部描述符表,所以设置 LDTR;
(4)经调用门进入 32 位过渡代码段;
(5)建立返回 3 级演示代码段的环境;
(6)利用 RET 指令转移到 3 级的演示代码段。为了演示外层程序通过调用门调用内层程序,要使 CPL>0。实例先通过段间返回指令

RET 从特权级 0 变换到特权级 3 的演示代码段。在特权级 3 下,通过调用门调用 1 级的子程序。随着执行段间返回指令 RET,又回到 3级的演示代码段;

(7)在 3 级的演示代码段中,经调用门转移到 0 级的 32 位过渡代码段;(8)直接转 0 级的临时代码段;
(9)准备返回实模式;
(10)切换回实模式;

(11)实模式下的恢复工作。

2.源程序组织和清单

实例四由如下部分组成:

(1)全局描述符表 GDT。GDT 含有演示任务的 TSS 段描述符和 LDT 段描述符,此外还含有临时代码段的描述符、规范数据段描述符和视频缓冲区段描述符。

(2)演示任务的 LDT 段。它含有除临时代码段外的其它代码段的描述符和演示任务各级堆栈段描述符,还含有 3 个调用门。

(3)演示任务的 TSS 段。
(4)演示任务的 0 级、1 级和 3 级堆栈段。
(5)显示子程序段。32 位代码段,特权级 1。

(6)演示代码段。32 位代码段,特权级 3。

(7)过渡代码段。32 位段,特权级 0。
(8)临时代码段。16 位段,特权级 0。
(9)实模式下的数据和代码段。该实例的逻辑功能是显示演示代码段执行时的当前特权级 CPL和TI。源程序清单如下: 


;windows;16位偏移的段间直接转移指令的宏定义(在16位代码段中使用);----------------------------------------------------------------------------JUMP16 MACRO Selector,OffsetvDB 0eah ;操作码DW Offsetv ;16位偏移量DW Selector ;段值或段选择子ENDM;----------------------------------------------------------------------------;32位偏移的段间直接转移指令的宏定义(在32位代码段中使用);----------------------------------------------------------------------------COMMENT <JUMP32>JUMP32 MACRO Selector,OffsetvDB 0eah ;操作码DD OffsetvDW Selector ;段值或段选择子ENDM<JUMP32>;-------------------------------------------------JUMP32 MACRO Selector,OffsetvDB 0eah ;操作码DW OffsetvDW 0DW Selector ;段值或段选择子ENDM;----------------------------------------------------------------------------;门描述符结构类型定义;----------------------------------------------------------------------------Gate STRUCOffsetL DW 0 ;32位偏移的低16位Selector DW 0 ;选择子DCount DB 0 ;双字计数GType DB 0 ;类型OffsetH DW 0 ;32位偏移的高16位Gate ENDS;----------------------------------------------------------------------------;16位偏移的段间调用指令的宏定义(在16位代码段中使用);----------------------------------------------------------------------------CALL16 MACRO Selector,OffsetvDB 9ah ;操作码DW Offsetv ;16位偏移量DW Selector ;段值或段选择子ENDM;----------------------------------------------------------------------------;32位偏移的段间调用指令的宏定义(在32位代码段中使用);----------------------------------------------------------------------------COMMENT <CALL32>CALL32 MACRO Selector,OffsetvDB 9ah ;操作码DD OffsetvDW Selector ;段值或段选择子ENDM<CALL32>;-------------------------------------------------CALL32 MACRO Selector,OffsetvDB 9ah ;操作码DW OffsetvDW 0DW Selector ;段值或段选择子ENDM;----------------------------------------------------------------------------;存储段描述符结构类型定义;----------------------------------------------------------------------------Desc STRUCLimitL DW 0 ;段界限(BIT0-15)BaseL DW 0 ;段基地址(BIT0-15)BaseM DB 0 ;段基地址(BIT16-23)Attributes DB 0 ;段属性LimitH DB 0 ;段界限(BIT16-19)(含段属性的高4位)BaseH DB 0 ;段基地址(BIT24-31)Desc ENDS;----------------------------------------------------------------------------;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器);----------------------------------------------------------------------------PDesc STRUCLimit DW 0 ;16位界限Base DD 0 ;32位基地址PDesc ENDS;----------------------------------------------------------------------------;存储段描述符类型值说明;----------------------------------------------------------------------------D32 EQU 40h ;32位代码段标志ATDR EQU 90h ;存在的只读数据段类型值ATDW EQU 92h ;存在的可读写数据段属性值ATDWA EQU 93h ;存在的已访问可读写数据段类型值ATCE EQU 98h ;存在的只执行代码段属性值ATCER EQU 9ah ;存在的可执行可读代码段属性值;----------------------------------------------------------------------------;系统段描述符类型值说明;----------------------------------------------------------------------------ATLDT EQU 82h ;局部描述符表段类型值;----------------------------------------------------------------------------;DPL值说明;----------------------------------------------------------------------------DPL0 EQU 00h ;DPL=0DPL1 EQU 20h ;DPL=1DPL2 EQU 40h ;DPL=2DPL3 EQU 60h ;DPL=3;----------------------------------------------------------------------------;RPL值说明;----------------------------------------------------------------------------RPL0 EQU 00h ;RPL=0RPL1 EQU 01h ;RPL=1RPL2 EQU 02h ;RPL=2RPL3 EQU 03h ;RPL=3;----------------------------------------------------------------------------;----------------------------------------------------------------------------;其它常量值说明;----------------------------------------------------------------------------TIL EQU 04h ;TI=1(局部描述符表标志)AT386TSS EQU 89h ;可用386任务状态段类型值AT386CGate EQU 8ch ;386调用门类型值;----------------------------------------------------------------------------.386pGDTSeg          SEGMENT PARA USE16                ;全局描述符表数据段(16位);----------------------------------------------------------------------------                ;全局描述符表GDT             LABEL   BYTE                ;空描述符DUMMY           Desc    <>                ;规范段描述符Normal          Desc    <0ffffh,,,ATDW,,>                ;视频缓冲区段描述符(DPL=3)VideoBuf        Desc    <07fffh,8000h,0bh,ATDW+DPL3,,>;----------------------------------------------------------------------------EFFGDT          LABEL   BYTE                ;任务状态段TSS描述符DemoTSS         Desc    <DemoTSSLen-1,DemoTSSSeg,,AT386TSS,,>                ;局部描述符表段的描述符DemoLDTD        Desc    <DemoLDTLen-1,DemoLDTSeg,,ATLDT,,>                ;临时代码段描述符TempCode        Desc    <0ffffh,TempCodeSeg,,ATCE,,>  ;注意它的存储段描述符;----------------------------------------------------------------------------GDTLen          =       $-GDT                     ;全局描述符表长度GDNum           =       ($-EFFGDT)/(SIZE Desc)    ;需特殊处理的描述符数;----------------------------------------------------------------------------Normal_Sel      =       Normal-GDT                ;规范段描述符选择子Video_Sel       =       VideoBuf-GDT              ;视频缓冲区段描述符选择子;----------------------------------------------------------------------------DemoTSS_Sel     =       DemoTSS-GDT               ;任务状态段描述符选择子DemoLDT_Sel     =       DemoLDTD-GDT              ;局部描述符表段的选择子TempCode_Sel    =       TempCode-GDT              ;临时代码段的选择子;----------------------------------------------------------------------------GDTSeg          ENDS                              ;全局描述符表段定义结束;----------------------------------------------------------------------------DemoLDTSeg      SEGMENT PARA USE16                ;局部描述符表数据段(16位);----------------------------------------------------------------------------DemoLDT         LABEL   BYTE                      ;局部描述符表                ;0级堆栈段描述符(32位段)DemoStack0      Desc    <DemoStack0Len-1,DemoStack0Seg,,ATDW+DPL0,D32,>                ;1级堆栈段描述符(32位段)DemoStack1      Desc    <DemoStack1Len-1,DemoStack1Seg,,ATDW+DPL1,D32,>                ;3级堆栈段描述符(16位段)DemoStack3      Desc    <DemoStack3Len-1,DemoStack3Seg,,ATDW+DPL3,,>                ;代码段描述符(32位段,DPL=3)DemoCode        Desc    <DemoCodeLEN-1,DemoCodeSeg,,ATCE+DPL3,D32,>                ;过渡代码段描述符(32位段)T32Code         Desc    <T32CodeLen-1,T32CodeSeg,,ATCE,D32,>                ;显示子程序代码段描述符(32位段,DPL=1)EchoSubR        Desc    <EchoSubRLen-1,EchoSubRSeg,,ATCER+DPL1,D32,>;----------------------------------------------------------------------------DemoLDNum       =       ($-DemoLDT)/(SIZE Desc);----------------------------------------------------------------------------                ;0级堆栈描述符选择子(RPL=0)DemoStack0_Sel  =       DemoStack0-DemoLDT+TIL+RPL0                ;1级堆栈描述符选择子(RPL=1)DemoStack1_Sel  =       DemoStack1-DemoLDT+TIL+RPL1                ;3级堆栈描述符选择子(RPL=3)DemoStack3_Sel  =       DemoStack3-DemoLDT+TIL+RPL3                ;代码段描述符选择子(RPL=3)DemoCode_Sel    =       DemoCode-DemoLDT+TIL+RPL3                ;过渡代码段描述符选择子T32Code_Sel     =       T32Code-DemoLDT+TIL                ;显示子程序代码段描述符选择子(RPL=1)Echo_Sel1       =       EchoSubR-DemoLDT+TIL+RPL1                ;显示子程序代码段描述符选择子(RPL=3)Echo_Sel3       =       EchoSubR-DemoLDT+TIL+RPL3;----------------------------------------------------------------------------                ;指向过渡代码段内T32Begin点的调用门(DPL=0)ToT32GateA      Gate    <T32Begin,T32Code_Sel,,AT386CGate,>                ;指向过渡代码段内T32End点的调用门(DPL=3)ToT32GateB      Gate    <T32End,T32Code_Sel,,AT386CGate+DPL3,>                ;指向显示子程序代码段的调用门(DPL=3)ToEchoGate      Gate    <EchoSUB,Echo_Sel3,,AT386CGate+DPL3,>;----------------------------------------------------------------------------DemoLDTLen      =       $-DemoLDT;----------------------------------------------------------------------------                ;指向过渡代码段内T32Begin点的调用门的选择子ToT32A_Sel      =       ToT32GateA-DemoLDT+TIL                ;指向过渡代码段内T32End点的调用门的选择子ToT32B_Sel      =       ToT32GateB-DemoLDT+TIL                ;显示子程序调用门的选择子ToEcho_Sel      =       ToEchoGate-DemoLDT+TIL;----------------------------------------------------------------------------DemoLDTSeg      ENDS                              ;局部描述符表段定义结束;----------------------------------------------------------------------------DemoTSSSeg      SEGMENT PARA USE16                ;任务状态段TSS;----------------------------------------------------------------------------                DD      0                         ;Back                DW      DemoStack0Len,0            ;0级堆栈指针                DW      DemoStack0_Sel,0            ;初始化                DW      DemoStack1Len,0             ;1级堆栈指针                DW      DemoStack1_Sel,0            ;初始化                DD      0                         ;2级堆栈指针                DD      0                         ;未初始化                DD      0                         ;CR3                DD      0                         ;EIP                DD      0                         ;EFLAGS                DD      0                         ;EAX                DD      0                         ;ECX                DD      0                         ;EDX                DD      0                         ;EBX                DD      0                         ;ESP                DD      0                         ;EBP                DD      0                         ;ESI                DD      0                         ;EDI                DW      ?,0                         ;ES                DW      ?,0                         ;CS                DW      ?,0                         ;SS                DW      ?,0                         ;DS                DW      ?,0                         ;FS                DW      ?,0                         ;GS                DW      DemoLDT_Sel,0               ;LDT                DW      0                         ;调试陷阱标志                DW      $+2                       ;指向I/O许可位图                DB      0ffh                    ;I/O许可位图结束标志;----------------------------------------------------------------------------DemoTSSLen      =       $-DemoTSSSeg;----------------------------------------------------------------------------DemoTSSSeg      ENDS                              ;任务状态段TSS结束;----------------------------------------------------------------------------DemoStack0Seg   SEGMENT DWORD STACK USE32         ;0级堆栈段(32位段)DemoStack0Len   =       512                DB      DemoStack0Len DUP(0)DemoStack0Seg   ENDS                              ;0级堆栈段结束;----------------------------------------------------------------------------DemoStack1Seg   SEGMENT DWORD STACK USE32         ;1级堆栈段(32位段)DemoStack1Len   =       512                DB      DemoStack1Len DUP(0)DemoStack1Seg   ENDS                              ;1级堆栈段结束;----------------------------------------------------------------------------DemoStack3Seg   SEGMENT DWORD STACK USE16         ;3级堆栈段(16位段)DemoStack3Len   =       512                DB      DemoStack3Len DUP(?)DemoStack3Seg   ENDS                              ;3级堆栈段结束;----------------------------------------------------------------------------EchoSubRSeg     SEGMENT PARA USE32                ;显示子程序代码段(32位,1级)                ASSUME  CS:EchoSubRSeg;----------------------------------------------------------------------------Message         DB      'CPL=  TI=',0                  ;显示信息(该代码段可读);----------------------------------------------------------------------------EchoSub         PROC    FAR                cld                push    ebp                mov     ebp,esp                mov     ax,Echo_Sel1                ;该代码段是可读段                mov     ds,ax                     ;采用RPL=1的选择子                mov     ax,Video_Sel                mov     es,ax                mov     edi,320                   ;信息显示位置                mov     esi,OFFSET Message                mov     ah,4eh                    ;置显示属性(红底黄字)EchoSub1:        lodsb                or      al,al                jz      EchoSub2                stosw                jmp     EchoSub1EchoSub2:         mov     al,[ebp+8]               ;从堆栈中取调用程序的选择子                and     al,3                      ;调用程序的CPL在CS的RPL字段                add     al,'0'                mov     ah,4eh                    ;置显示属性(红底黄字)   sub     edi,10                stosw   add     edi,8   mov     al,[ebp+8]               ;从堆栈中取调用程序的选择子                and     al,1                     ;调用程序TI字段                add     al,'0'                mov     ah,4eh     stosw                pop     ebp                retfEchoSub         ENDP;----------------------------------------------------------------------------EchoSubRLen     =       $-EchoSubRSeg;----------------------------------------------------------------------------EchoSubRSeg     ENDS                              ;显示子程序代码段结束;----------------------------------------------------------------------------DemoCodeSeg     SEGMENT PARA USE32                ;32位代码段(3级)                ASSUME  CS:DemoCodeSeg;----------------------------------------------------------------------------DemoBegin       PROC    FAR                CALL32  ToEcho_Sel,0              ;显示当前特权级(变换到1级)                CALL32  ToT32B_Sel,0              ;转到过渡代码段(变换到0级)DemoBegin       ENDPDemoCodeLen     =       $-DemoCodeSeg;----------------------------------------------------------------------------DemoCodeSeg     ENDS                              ;32位代码段结束;----------------------------------------------------------------------------T32CodeSeg      SEGMENT PARA USE32                ;32位过渡代码段(0级)                ASSUME  CS:T32CodeSeg;----------------------------------------------------------------------------T32Begin        PROC    FAR                mov     ax,DemoStack0_Sel         ;建立0级堆栈                mov     ss,ax                mov     esp,DemoStack0Len                push    DWORD PTR DemoStack3_Sel  ;压入3级堆栈指针                push    DemoStack3Len                push    DWORD PTR DemoCode_SEL    ;压入入口点                push    OFFSET DemoBegin                retf                              ;利用RET实现转3级的演示代码T32Begin        ENDP;----------------------------------------------------------------------------T32End          PROC    FAR                JUMP32  TempCode_Sel,<OFFSET ToReal>T32End          ENDPT32CodeLen      =       $-T32CodeSeg;----------------------------------------------------------------------------T32CodeSeg      ENDS;----------------------------------------------------------------------------TempCodeSeg     SEGMENT PARA USE16                ;16位临时代码段(0级)                ASSUME  CS:TempCodeSeg;----------------------------------------------------------------------------Virtual         PROC    FAR                mov     ax,DemoTSS_Sel            ;装载TR                ltr     ax                mov     ax,DemoLDT_Sel            ;装载LDTR                lldt    ax                JUMP16  ToT32A_Sel,0              ;通过调用门转过渡段ToReal:          mov     ax,Normal_Sel             ;准备切换回实模式                mov     ds,ax                mov     es,ax                mov     fs,ax                mov     gs,ax                mov     ss,ax                mov     eax,cr0                and     al,11111110b                mov     cr0,eax                JUMP16  <SEG Real>,<OFFSET Real>Virtual         ENDP;----------------------------------------------------------------------------TempCodeLen     =       $-TempCodeSegTempCodeSeg     ENDS;============================================================================RDataSeg        SEGMENT PARA USE16                ;实方式数据段VGDTR           PDesc   <GDTLen-1,>               ;GDT伪描述符SPVar           DW      ?                         ;用于保存实方式下的SPSSVar           DW      ?                         ;用于保存实方式下的SSRDataSeg        ENDS;----------------------------------------------------------------------------RCodeSeg        SEGMENT PARA USE16                ASSUME  CS:RCodeSeg,DS:RDataSeg;----------------------------------------------------------------------------Start           PROC                mov     ax,RDataSeg                mov     ds,ax                cld                CALL    InitGDT                   ;初始化全局描述符表GDT                mov     ax,DemoLDTSeg                mov     fs,ax                mov     si,OFFSET DemoLDT                mov     cx,DemoLDNum                CALL    InitLDT                   ;初始化局部描述符表LDT                mov     SSVar,ss                mov     SPVar,sp                lgdt    FWORD PTR VGDTR           ;装载GDTR并切换到保护方式                cli                mov     eax,cr0                or      al,1                mov     cr0,eax                JUMP16  <TempCode_Sel>,<OFFSET Virtual>Real:            mov     ax,RDataSeg                mov     ds,ax                lss     sp,DWORD PTR SPVar        ;又回到实方式                sti                mov     ax,4c00h                int     21hStart           ENDP;----------------------------------------------------------------------------;初始化GDT表InitGDT         PROC                push    ds                mov     ax,GDTSeg                mov     ds,ax                mov     cx,GDNum                mov     si,OFFSET EFFGDTInitG:           mov     ax,[si].BaseL                movzx   eax,ax                shl     eax,4                shld    edx,eax,16                mov     WORD PTR [si].BaseL,ax                mov     BYTE PTR [si].BaseM,dl                mov     BYTE PTR [si].BaseH,dh                add     si,SIZE Desc                loop    InitG                pop     ds                mov     bx,16                mov     ax,GDTSeg                mul     bx                mov     WORD PTR VGDTR.Base,ax                mov     WORD PTR VGDTR.Base+2,dx                retInitGDT         ENDP;----------------------------------------------------------------------------;初始化LDT表;入口参数:FS:SI=第一个要初始化的描述符,CX=要初始化的描述符数;----------------------------------------------------------------------------InitLDT         PROCILDT:            mov     ax,WORD PTR FS:[si].BaseL                movzx   eax,ax                shl     eax,4                shld    edx,eax,16                mov     WORD PTR fs:[si].BaseL,ax                mov     BYTE PTR fs:[si].BaseM,dl                mov     BYTE PTR fs:[si].BaseH,dh                add     si,SIZE Desc                loop    ILDT                retInitLDT         ENDP;----------------------------------------------------------------------------RCodeSeg        ENDS                END     Start  
程序中部分片段的背景和实现方法在前面的实例中做过介绍,下面主要就如何实现任务内特权级变换做些说明:                                                            

(1)通过段间返回指令实现特权级变换

实例在两处使用段间返回指令实现任务内的特权级变换。一处是在 0 级的过渡代码段中用段间 RET 指令从特权级 0 变换到特权级 3的演示代码段。该处 RET 指令并不对应 CALL 指令。实例从实模式切换到保护模式后 CPL=0。为了演示如何通过调用门调用内层程序,要设法使 CPL>0。为此,实例先建立一个已发生的从外层到内层变换的环境,即按上图所示在当前堆栈(0 级堆栈)中放入外层堆栈的指

针和外层演示程序的入口指针,形成一个如下图所示的 0 级堆栈,无需传递参数。然后,执行段间返回指令 RET,从堆栈中弹出 3 级演示代码段的选择子,RPL=3,而当时 CPL=0,所以导致向外层变换特权级,从 0 级的过渡代码段变换到 3 级的演示代码段,同时切换到3 级堆栈。

另一处是从 1 级的显示子程序 EchoSub 返回到 3 级的演示程序段。该处的 RET 指令与演示程序中使用的通过调用门的段间调用指令 CALL 相对应,执行段间返回指令 RET 时的 1 级堆栈也如上图所示,其中的返回地址和外层栈指针由 CALL 指令压入。

(2)通过调用门实现特权级变换

实例在两处使用了段间调用指令,通过调用门实现特权级的变换。一处是 3 级演示代码通过调用门 ToEchoGate 调用 1 级的显示子程序。调用门 ToEchoGate 自身的 DPL=3,只有这样,3 级的演示代码才能够使用该调用门。由于调用门内的选择子 Echo_Sel3 所指示的显示子程序代码段描述符 DPL=1,而当时 CPL=3,所以引起从外层特权级向内层特权级的变换,使 CPL=1。同时形成如上图所示的 1级堆栈。虽然调用门内的选择子 Echo_Sel3 的 RPL=3,大于目标代码段的 DPL,但没有关系,因为在通过调用门转移时,门内指示目标代码段的选择子 RPL 总被作 0 对待。

另一处是 3 级演示代码还通过调用门 ToT32GateB 调用了 0 级的过渡代码。该处使用的调用门描述符 DPL 也等于 3。由于调用门内的选择子 T32Code_Sel 所指示的过渡代码段描述符的 DPL=0,而当时 CPL=3,所以引起从 3 特权级向 0 特权级的变换,使 CPL=0。同时形成如上图所示的 0 级堆栈。但该处的调用实际上是 “有去无回”的,调用的目的是转移到 0 级的过渡代码,准备返回实模式。由于从 3级的演示代码到 0 级的过渡代码要发生特权级变换,所以不能使用转移指令 JMP,必须使用调用指令 CALL。

(3)通过调用门实现无特权级变换
在临时代码段中,使用调用门 ToT32GateA 转移到过渡代码段。尽管调用门内的选择子 T32Code_Sel 所指示的过渡代码段描述符的

DPL=0,但当时 CPL=0,所以不发生特权级变换。正是这个原因,才可以使用段间转移指令 JMP。

(4)子程序EchoSub的实现

子程序 EchoSub 的功能是显示调用程序执行时的特权级。调用程序的执行特权级在代码段寄存器 CS 内选择子的 RPL 字段,在调用EchoSub 时,CS 寄存器的内容被压入堆栈。子程序从堆栈取得调用程序的代码段选择子,再从中分离出 RPL 就可得调用程序的执行特权级。

(5)装载任务状态段寄存器TR

在任务内发生特权级变换时堆栈也随着自动切换,外层堆栈指针保存在内层堆栈中,而内层堆栈指针存放在当前任务的 TSS 中。所以,在从外层向内层变换时,要访问 TSS(从内层向外层转移时不需要访问 TSS,而只需内层栈中保存的栈指针)。实例在进入保护模式下的临时代码段后,通过如下两条指令装载任务状态段寄存器 TR,使其指向已预置好的任务的 TSS:

mov  ax,DemoTSS_Sel

ltr    ax
LTR 指令是专门用于装载任务状态段寄存器 TR 的指令。该指令的操作数是对应 TSS 段描述符的选择子。LTR 指令从 GDT 中取出

相应的 TSS 段描述符,把 TSS 段描述符的基地址和界限等信息装入 TR 的高速缓冲寄存器中。 


我要说的:
如果从开始慢慢学到这里的话,看这个代码应该没什么大问题的,说一下调用栈里面的选择子显示CPL,TI值的代码

EchoSub2:       mov     al,[ebp+8]               ;从堆栈中取调用程序的选择子                and     al,3                      ;调用程序的CPL在CS的RPL字段                add     al,'0'                mov     ah,4eh                    ;置显示属性(红底黄字)sub     edi,10                stoswadd     edi,8mov     al,[ebp+8]               ;从堆栈中取调用程序的选择子                and     al,1                     ;调用程序TI字段                add     al,'0'                mov     ah,4eh  stosw                pop     ebp                retf

为什么是ebp+8?调用这段代码的是

CALL32  ToEcho_Sel,0              ;显示当前特权级(变换到1级)

该代码所在段是3级,转到1级代码段,发生特权级变换,会切换栈,将当前的段选择子(被扩展为32位)和偏移(32位)压入3级栈,进入到1级代码段,由外层栈切换到内层栈,然后进行参数复制,复制开始压入的段选择子和偏移,然后push ebp,1级栈里面存的就是这些了示意图如下:




目前还有一个问题未弄明白:

TempCode        Desc    <0ffffh,TempCodeSeg,,ATCE,,>  ;注意它的存储段描述符

我在测试的时候发现它的选择子的段界限不能是TempCodeLen-1(或TempCodeLen),前者直接让DOS崩溃了,TempCodeLen+4到是可以正常输出,但是就是没有办法正常返回,段界限为0ffffh就可以正常执行并返回,难道是存在访问越界,被处理器终止或者访问出错吗,这点现在我还没有很好的想明白。

运行结果:



下面是书上的代码(Linux平台)在特权级切换的同时输出字符(这里我做了一点小改动)


这里的value是特权级为3的选择子的输出,本来打算直接在ring3代码段中输出,后来发现DOS会崩溃,突然才发现数据段CPL=0,而ring3的CPL=3,不能访问数据段,所以就干脆放到了CPL=0的代码段(LABEL_SEG_CODE32)中输出了。

%macro Descriptor 3dw%2 & 0FFFFh; 段界限 1(2 字节)dw%1 & 0FFFFh; 段基址 1(2 字节)db(%1 >> 16) & 0FFh; 段基址 2(1 字节)dw((%2 >> 8) & 0F00h) | (%3 & 0F0FFh); 属性 1 + 段界限 2 + 属性 2(2 字节)db(%1 >> 24) & 0FFh; 段基址 3(1 字节)%endmacro ; 共 8 字节%macro Gate 4dw(%2 & 0FFFFh); 偏移 1(2 字节)dw%1; 选择子(2 字节)dw(%3 & 1Fh) | ((%4 << 8) & 0FF00h); 属性(2 字节)dw((%2 >> 16) & 0FFFFh); 偏移 2(2 字节)%endmacro ; 共 8 字节DA_32EQU4000h; 32 位段DA_DPL3EQU  60h; DPL = 3; 存储段描述符类型值说明;----------------------------------------------------------------------------DA_DREQU90h; 存在的只读数据段类型值DA_DRWEQU92h; 存在的可读写数据段属性值DA_DRWAEQU93h; 存在的已访问可读写数据段类型值DA_CEQU98h; 存在的只执行代码段属性值;----------------------------------------------------------------------------; 系统段描述符类型值说明;----------------------------------------------------------------------------DA_LDTEQU  82h; 局部描述符表段类型值DA_TaskGateEQU  85h; 任务门类型值DA_386TSSEQU  89h; 可用 386 任务状态段类型值DA_386CGateEQU  8Ch; 386 调用门类型值; 选择子类型值说明; 其中:;       SA_  : Selector AttributeSA_RPL0EQU0; ┓SA_RPL1EQU1; ┣ RPLSA_RPL2EQU2; ┃SA_RPL3EQU3; ┛SA_TIGEQU0; ┓TISA_TILEQU4; ┛org0100hjmpLABEL_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   ;非一致,32LABEL_DESC_CODE16:     Descriptor 0,            0ffffh, DA_C   ;非一致,16LABEL_DESC_CODE_DEST:  Descriptor 0,  SegCodeDestLen-1, DA_C+DA_32   ;非一致,32LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3LABEL_DESC_DATA:       Descriptor 0,     DataLen-1, DA_DRW             ;DataLABEL_DESC_STACK:      Descriptor 0,        TopOfStack, DA_DRWA+DA_32   ;Stack,32LABEL_DESC_STACK3:     Descriptor 0,       TopOfStack3, DA_DRWA+DA_32+DA_DPL3LABEL_DESC_LDT:        Descriptor 0,          LDTLen-1, DA_LDT   ;LDTLABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS   ;TSSLABEL_DESC_VIDEO:      Descriptor 0B8000h,      0ffffh, DA_DRW+DA_DPL3; 门                                            目标选择子,       偏移, DCount, 属性LABEL_CALL_GATE_TEST:Gate  SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3; GDT 结束GdtLenequ$ - LABEL_GDT; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址; GDT 选择子SelectorNormalequLABEL_DESC_NORMAL- LABEL_GDTSelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorCode16equLABEL_DESC_CODE16- LABEL_GDTSelectorCodeDestequLABEL_DESC_CODE_DEST- LABEL_GDTSelectorCodeRing3equLABEL_DESC_CODE_RING3- LABEL_GDT + SA_RPL3SelectorDataequLABEL_DESC_DATA- LABEL_GDTSelectorStackequLABEL_DESC_STACK- LABEL_GDTSelectorStack3equLABEL_DESC_STACK3- LABEL_GDT + SA_RPL3SelectorLDTequLABEL_DESC_LDT- LABEL_GDTSelectorTSSequLABEL_DESC_TSS- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDTSelectorCallGateTestequLABEL_CALL_GATE_TEST- LABEL_GDT + SA_RPL3; END of [SECTION .gdt][SECTION .data1] ; 数据段ALIGN32[BITS32]LABEL_DATA:SPValueInRealModedw0; 字符串PMMessage:db"In Protect Mode now. ^-^", 0; 进入保护模式后显示此字符串OffsetPMMessageequPMMessage - $$StrTest:db"value=", 0OffsetStrTestequStrTest - $$DataLenequ$ - LABEL_DATA; END of [SECTION .data1]; 全局堆栈段[SECTION .gs]ALIGN32[BITS32]LABEL_STACK:times 512 db 0TopOfStackequ$ - LABEL_STACK - 1; END of [SECTION .gs]; 堆栈段ring3[SECTION .s3]ALIGN32[BITS32]LABEL_STACK3:times 512 db 0TopOfStack3equ$ - LABEL_STACK3 - 1; END of [SECTION .s3]; TSS ---------------------------------------------------------------------------------------------[SECTION .tss]ALIGN32[BITS32]LABEL_TSS:DD0; BackDDTopOfStack; 0 级堆栈DDSelectorStack; DD0; 1 级堆栈DD0; DD0; 2 级堆栈DD0; DD0; CR3DD0; EIPDD0; EFLAGSDD0; EAXDD0; ECXDD0; EDXDD0; EBXDD0; ESPDD0; EBPDD0; ESIDD0; EDIDD0; ESDD0; CSDD0; SSDD0; DSDD0; FSDD0; GSDD0; LDTDW0; 调试陷阱标志DW$ - LABEL_TSS + 2; I/O位图基址DB0ffh; I/O位图结束标志TSSLenequ$ - LABEL_TSS; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hmov[LABEL_GO_BACK_TO_REAL+3], axmov[SPValueInRealMode], sp; 初始化 16 位代码段描述符movax, csmovzxeax, axshleax, 4addeax, LABEL_SEG_CODE16movword [LABEL_DESC_CODE16 + 2], axshreax, 16movbyte [LABEL_DESC_CODE16 + 4], almovbyte [LABEL_DESC_CODE16 + 7], ah; 初始化 32 位代码段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE32movword [LABEL_DESC_CODE32 + 2], axshreax, 16movbyte [LABEL_DESC_CODE32 + 4], almovbyte [LABEL_DESC_CODE32 + 7], ah; 初始化测试调用门的代码段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE_DESTmovword [LABEL_DESC_CODE_DEST + 2], axshreax, 16movbyte [LABEL_DESC_CODE_DEST + 4], almovbyte [LABEL_DESC_CODE_DEST + 7], ah; 初始化数据段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_DATAmovword [LABEL_DESC_DATA + 2], axshreax, 16movbyte [LABEL_DESC_DATA + 4], almovbyte [LABEL_DESC_DATA + 7], ah; 初始化堆栈段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_STACKmovword [LABEL_DESC_STACK + 2], axshreax, 16movbyte [LABEL_DESC_STACK + 4], almovbyte [LABEL_DESC_STACK + 7], ah; 初始化堆栈段描述符(ring3)xoreax, eaxmovax, dsshleax, 4addeax, LABEL_STACK3movword [LABEL_DESC_STACK3 + 2], axshreax, 16movbyte [LABEL_DESC_STACK3 + 4], almovbyte [LABEL_DESC_STACK3 + 7], ah; 初始化 LDT 在 GDT 中的描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_LDTmovword [LABEL_DESC_LDT + 2], axshreax, 16movbyte [LABEL_DESC_LDT + 4], almovbyte [LABEL_DESC_LDT + 7], ah; 初始化 LDT 中的描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_CODE_Amovword [LABEL_LDT_DESC_CODEA + 2], axshreax, 16movbyte [LABEL_LDT_DESC_CODEA + 4], almovbyte [LABEL_LDT_DESC_CODEA + 7], ah; 初始化Ring3描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_CODE_RING3movword [LABEL_DESC_CODE_RING3 + 2], axshreax, 16movbyte [LABEL_DESC_CODE_RING3 + 4], almovbyte [LABEL_DESC_CODE_RING3 + 7], ah; 初始化 TSS 描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_TSSmovword [LABEL_DESC_TSS + 2], axshreax, 16movbyte [LABEL_DESC_TSS + 4], almovbyte [LABEL_DESC_TSS + 7], ah; 为加载 GDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, LABEL_GDT; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt[GdtPtr]; 关中断cli; 打开地址线A20inal, 92horal, 00000010bout92h, al; 准备切换到保护模式moveax, cr0oreax, 1movcr0, eax; 真正进入保护模式jmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LABEL_REAL_ENTRY:; 从保护模式跳回到实模式就到了这里movax, csmovds, axmoves, axmovss, axmovsp, [SPValueInRealMode]inal, 92h; ┓andal, 11111101b; ┣ 关闭 A20 地址线out92h, al; ┛sti; 开中断movax, 4c00h; ┓int21h; ┛回到 DOS; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.[BITS32]LABEL_SEG_CODE32:movax, SelectorDatamovds, ax; 数据段选择子movax, SelectorVideomovgs, ax; 视频段选择子movax, SelectorStackmovss, ax; 堆栈段选择子movesp, TopOfStack; 下面显示一个字符串movah, 0Ch; 0000: 黑底    1100: 红字xoresi, esixoredi, edimovesi, OffsetPMMessage; 源数据偏移movedi, (80 * 10 + 0) * 2; 目的数据偏移。屏幕第 10 行, 第 0 列。cld.1:lodsbtestal, aljz.2mov[gs:edi], axaddedi, 2jmp.1.2:; 显示完毕callDispReturn;显示LABEL_CODE_RING3的CPL        mov     ax,SelectorDatamov ds,axmov esi,OffsetStrTestmov ax,SelectorVideomov es,axmov     ah,4eh                    ;置显示属性(红底黄字)EchoSub1: lodsb        or      al,al        jz      EchoSub2        stosw        jmp     EchoSub1EchoSub2:mov ax,SelectorCodeRing3                     and     al,3                      ;调用程序的CPL在CS的RPL字段        add     al,'0'        mov     ah,4eh                    ;置显示属性(红底黄字)        mov[gs:edi], ax; Load TSSmovax, SelectorTSSltrax; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。pushSelectorStack3pushTopOfStack3pushSelectorCodeRing3push0retf; Ring0 -> Ring3,历史性转移!将打印数字 '3'。; ------------------------------------------------------------------------DispReturn:pusheaxpushebxmoveax, edimovbl, 160divblandeax, 0FFhinceaxmovbl, 160mulblmovedi, eaxpopebxpopeaxret; DispReturn 结束---------------------------------------------------------SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32][SECTION .sdest]; 调用门目标段[BITS32]LABEL_SEG_CODE_DEST:movax, SelectorVideomovgs, ax; 视频段选择子(目的)movedi, (80 * 12 + 0) * 2; 屏幕第 12 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 红字moval, 'C'mov[gs:edi], ax; Load LDTmovax, SelectorLDTlldtaxjmpSelectorLDTCodeA:0; 跳入局部任务,将打印字母 'L'。SegCodeDestLenequ$ - LABEL_SEG_CODE_DEST; END of [SECTION .sdest]; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式[SECTION .s16code]ALIGN32[BITS16]LABEL_SEG_CODE16:; 跳回实模式:movax, SelectorNormalmovds, axmoves, axmovfs, axmovgs, axmovss, axmoveax, cr0andal, 11111110bmovcr0, eaxLABEL_GO_BACK_TO_REAL:jmp0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置成正确的值Code16Lenequ$ - LABEL_SEG_CODE16; END of [SECTION .s16code]; LDT[SECTION .ldt]ALIGN32LABEL_LDT:;                                         段基址       段界限     ,   属性LABEL_LDT_DESC_CODEA:Descriptor       0,     CodeALen - 1,   DA_C + DA_32; Code, 32 位LDTLenequ$ - LABEL_LDT; LDT 选择子SelectorLDTCodeAequLABEL_LDT_DESC_CODEA- LABEL_LDT + SA_TIL; END of [SECTION .ldt]; CodeA (LDT, 32 位代码段)[SECTION .la]ALIGN32[BITS32]LABEL_CODE_A:movax, SelectorVideomovgs, ax; 视频段选择子(目的)movedi, (80 * 13 + 0) * 2; 屏幕第 13 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 红字moval, 'L'mov[gs:edi], ax; 准备经由16位代码段跳回实模式jmpSelectorCode16:0CodeALenequ$ - LABEL_CODE_A; END of [SECTION .la]; CodeRing3[SECTION .ring3]ALIGN32[BITS32]LABEL_CODE_RING3:movax, SelectorVideomovgs, ax; 视频段选择子(目的)movedi, (80 * 14 + 0) * 2; 屏幕第 14 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 红字moval, '3'mov[gs:edi], axcallSelectorCallGateTest:0; 测试调用门(有特权级变换),将打印字母 'C'。                 SegCodeRing3Lenequ$ - LABEL_CODE_RING3; END of [SECTION .ring3]

在代码段LABEL_DESC_CODE32中有这么一段代码。

pushSelectorStack3pushTopOfStack3pushSelectorCodeRing3push0retf; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

后面三句语句都理解吧,就是把选择子,偏移压栈,利用retf指令返回,但是为什么要前两个语句了,这里需要回顾一下:

《Orange's 一个操作系统的实现》学习笔记--特权级代码段之间的转移(二)里面的内容,看下面图示:


有ring0到ring3转移过程中,会把调用者的ss,esp恢复,并且esp调整,废除开始前入栈的参数,但是我们这里没有从ring3进入ring0,返回从ring0直接进入ring3,所以这里我们需要自己手动的把目的栈的选择子和偏移压入栈,这样才能顺利执行,这里没有函数参数,所以返回后不用调整esp.



0 0
原创粉丝点击