《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转

http://blog.csdn.net/begginghard/article/details/7262901

1、特权级 2、一致代码段和非一致代码段 3、DPL、RPL、CPL分别代表的含义,存储在什么位置,以及它们之间的关系 4、不同特权级数据段之间的访问规则 5、不同特权级代码段之间的转移 6、代码段之间的转移对堆栈的影响 7、结合pmtest5.asm来见证不同特权级代码段之间的跳转

一、特权级
在IA32的分段机制下,特权级总共有4个特权级别,从高到低分别是0、1、2、3。数字越小表示的特权级越大。特权级如下图所示:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第1张图片

较为核心的代码和数据,将被存放在特权级较高的层级中。处理器将用这样的机制来避免低特权级的任务在不被允许的情况下访问位于高特权级的段。

 

二、一致代码段 和 非一致代码段 系统要安全,必须保证内核与用户程序分离开,内核要安全,必须不能被用户来打扰。但是有的时候,用户程序也是需要访问内核中的部分数据,那怎么办?

于是操作系统就将内核中的段分为共享的代码段和非共享的代码段两部分。 其中一致代码段就是操作系统拿出来被共享的代码段,可以被低特权级的用户直接访问的代码。 一致代码段的限制作用: (1)特权级高的代码段不允许访问特权级低的代码段:即内核态不允许调用用户态下的代码。 (2)特权级低的代码段可以访问特权级高的代码段,但是当前的特权级不发生变化。即:用户态可以访问内核态的代码,但是用户态仍然是用户态。

非一致代码段:为了避免低特权级的访问而被操作系统保护起来的系统代码,也就是非共享代码。 非一致代码段的限制作用: (1)只允许同特权级间访问 (2)绝对禁止不同级间访问,即:用户态不能访问内核态,内核态也不访问用户态。

下图为一致码段与非一致码段的访问规则:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第2张图片

 

三、CPL、DPL、RPL分别代表的含义,存储在什么位置,以及它们之间的关系 1、CPL(Current Privilege Level)是当前执行的程序或任务的特权级。它被存储在CS和SS的第0位和第1位上。通常情况下,CPL等于代码的段的特权级。在遇到一致代码段时,一致代码段可以被相同或者更低特权级的代码访问。当处理器访问一个与CPL特权级不同的一致代码段时,CPL不会被改变。

2、DPL(Descriptor Privilege Level):DPL表示段或者门的特权级,它被存储在段描述符或者门描述符的DPL字段中。当当前代码段试图访问一个段或者门时,DPL将会和CPL以及段或门选择子的RPL相比较,根据段或者门类型的不同,DPL将会被区别对待,下面介绍一下各种类型的段或者门的情况。 (1)数据段:DPL规定了可以访问此段的最低特权级。比如,一个数据段的DPL是1,那么只有运行在CPL为0或者1的程序才有权访问它。 (2)非一致代码段(不使用调用门的情况下):DPL规定访问此段的特权级。比如一个非一致代码段的特权级为0,那么只有CPL为0的程序才可以访问它。 (3)调用门:DPL规定了当前执行的程序或任务可以访问此调用门的最低特权级(这与数据段的规则是一致的)。 (4)一致代码段和通过调用门访问的非一致代码段:DPL规定了访问此段的最高特权级。比如,一个一致代码段的DPL是2,那么CPL为0和1的程序将无法访问此段。

3、RPL(Requested Privilege Level):RPL是通过选择子的第0位和第1位表现出来的。处理器通过检查RPL和CPL来确认一个访问请求是否合法。

四、不同特权级数据段之间的访问规则
数据段中DPL规定了可以访问此段的最低特权级,因此,对数据的访问,只要CPL和RPL都小于被访问的数据段的DPL就可以了,即CPL<=DPL和RPL<=DPL。

 

五、不同特权级代码段之间的转移 使用jmp或call指令可以实现下列4种转移 (1)目标操作数包含目标代码段的段选择子。 (2)目标操作数指向一个包含目标代码段选择子的调用门描述符。 (3)目标操作数指向一个包含目标代码段选择子的TSS。 (4)目标操作数指向一个任务门,这个任务门指向一个包含目标代码段选择子的TSS。 这4种方式可以看做是两大类,一类是通过jmp和call的直接转移(上述第一种),另一类是通过某个描述  符的间接转移(上述第2,3,4种)。

1、通过jmp或call进行直接转移

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第3张图片

 

2、通过调用门进行转移 (1)门描述符的结构

调用门描述符里面保存着目标代码段的段选择子,偏移量,以及属性。

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第4张图片

(2)调用门的使用方式

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第5张图片

假设我们想由代码A转移到代码B,运用一个调用门G,即调用门G中的目标选择子指向代码B的段。实际上,这个问题主要涉及这几个元素:CPL、RPL、代码B的DPL(记做DPL_B),调用门G的DPL(记做DPL_G)。
调用门使用时特权级检验的规则如下:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第6张图片

也就是说,通过调用门和call指令,可以实现从低特权级到高特权级的转移,无论目标代码段时一致的还是非一致的。
通过调用门和jmp指令,如果目标代码段是一致的,则可以实现从低特权级到高特权级的转移。如果目标代码段是非一致的,则只能实现相同特权级的转移。

 

六、代码段之间的转移对堆栈的影响

1、“长”跳转/调用 和 “短”跳转/调用 如果一个调用或跳转指令时段间而不是段内进行的,那么我们称之为“长”的(Far jmp/call),反之,如果在段内则是“短”的(Near jmp/call)。

那么长的和短的jmp或call有什么分别呢? 对于jmp而言,仅仅是结果不同罢了,短跳转对应段内,长跳转对应段间。 对于call来说,就比较复杂一些,因为call指令是会影响堆栈的,长调用和短调用对堆栈的影响是不同的。

下面我们讨论短调用对堆栈的影响,call指令执行时下一条指令的eip压栈,到ret指令执行时,这个eip会被从堆栈中弹出, 如下图所示:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第7张图片

 

这是短调用的情况。

下面我们讨论长调用对堆栈的影响,call指令执行时会将调用者的cs和eip压栈,到ret指令执行时,这个eip和cs会被从堆栈中弹出,如下图所示:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第8张图片

2、有特权级变换的转移对堆栈的影响
在不同特权级下的堆栈段不同,所以每一个任务最多可能在4个特权级间转移,所以,每个任务实际上需要4个堆栈。可是我们只有一个ss和一个esp,那么当发生堆栈切换,我们该从哪里获得其余堆栈的ss和esp呢?
解决这个问题,需要一个数据结构TSS(Task-State Stack),如图:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第9张图片

当堆栈发生切换时,内层的ss和esp就是从这里取得的,比如,我们当前所在的是ring3,当转移至ring1时,堆栈将被自动切换到由ss1和esp1指定的位置。由于只是在由外层转移到内层(低特权级到高特权级)切换时新堆栈才会从TSS中取得,所以TSS中没有位于最外层的ring3的堆栈信息。
下面让我们来看看整个的转移过程是怎么样的?

执行call前后堆栈段的变化:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第10张图片

 

(1)根据目标代码段的DPL(新的CPL)从TSS中选择应该切换至哪个ss和esp (2)从TSS中读取新的ss和esp。在这过程中如果发现ss、esp或者TSS界限错误都会导致无效TSS异常 (3)对ss描述符进行检验,如果发生错误,同样产生#TS异常 (4)暂时性地保存当前ss和esp的值 (5)加载新的ss和esp (6)将刚刚保存起来的ss和esp的值压入新栈 (7)从调用者堆栈中将参数复制到被调用者堆栈(新堆栈)中,复制参数的数目由调用门中Param Count一项来决定。 (8)如果Param Count是零的话,将不会复制参数。 (9)将当前的cs和eip压栈 (10)加载调用门中指定的新的cs和eip,开始执行被调用者过程。

执行ret前后堆栈段的变化:

《一个操作系统的实现》读书笔记--第三章---不同特权级代码段之间的跳转_第11张图片

(1)检查保存的cs中的RPL以判断返回时是否要变换特权级
(2)加载被调用者堆栈上的cs和eip(此时会进行代码段描述符和选择子类型和特权级检验)
(3)如果ret指令含有参数,则增加esp的值以跳过参数,然后esp将指向被保存过的调用者ss和esp。注意,ret的参数必须对应调用门中的Param Count的值
(4)加载ss和esp,切换到调用者堆栈,被调用者的ss和esp被丢弃。在这里将会进行ss描述符、esp、以及ss段描述符的检验
(5)如果ret指令含有参数,增加esp的值以跳过参数(此时已经在调用者堆栈中)
(6)检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(此规则不适合于一致代码段),那么一个空描述符会被加载到该寄存器中。
综上所述,使用调用门的过程实际上分为两部分,一部分是从低特权级到高特权级,通过调用门和call指令来实现;另一部分则是从高特权级到低特权级,通过ret指令来实现。

七、结合pmtest5.asm来见证不同特权级代码段之间的跳转

  1. ; ==========================================  
  2. ; pmtest5.asm  
  3. ; 编译方法:nasm pmtest5.asm -o pmtest5.com  
  4. ; ==========================================  
  5.   
  6. %include    "pm.inc"    ; 常量, 宏, 以及一些说明  
  7.   
  8. org 0100h  
  9.     jmp LABEL_BEGIN  
  10.   
  11. [SECTION .gdt]  
  12. ; GDT  
  13. ;                                         段基址,         段界限     , 属性  
  14. LABEL_GDT:      Descriptor         0,                   0, 0            ; 空描述符  
  15. LABEL_DESC_NORMAL:  Descriptor         0,              0ffffh, DA_DRW           ; Normal 描述符  
  16. LABEL_DESC_CODE32:  Descriptor         0,    SegCode32Len - 1, DA_C + DA_32     ; 非一致代码段, 32  
  17. LABEL_DESC_CODE16:  Descriptor         0,              0ffffh, DA_C         ; 非一致代码段, 16  
  18. LABEL_DESC_CODE_DEST:   Descriptor         0,  SegCodeDestLen - 1, DA_C + DA_32     ; 非一致代码段, 32  
  19. LABEL_DESC_CODE_RING3:  Descriptor         0, SegCodeRing3Len - 1, DA_C + DA_32 + DA_DPL3   ; 非一致代码段, 32  
  20. LABEL_DESC_DATA:    Descriptor         0,     DataLen - 1, DA_DRW           ; Data  
  21. LABEL_DESC_STACK:   Descriptor         0,          TopOfStack, DA_DRWA + DA_32      ; Stack, 32 位  
  22. LABEL_DESC_STACK3:  Descriptor         0,         TopOfStack3, DA_DRWA + DA_32 + DA_DPL3; Stack, 32 位  
  23. LABEL_DESC_TSS:     Descriptor         0,          TSSLen - 1, DA_386TSS        ; TSS  
  24. LABEL_DESC_VIDEO:   Descriptor   0B8000h,              0ffffh, DA_DRW + DA_DPL3     ; 显存首地址  
  25.   
  26. ; 门                                            目标选择子,       偏移, DCount, 属性  
  27. LABEL_CALL_GATE_TEST:   Gate          SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3  
  28. ; GDT 结束  
  29.   
  30. GdtLen      equ $ - LABEL_GDT   ; GDT长度  
  31. GdtPtr      dw  GdtLen - 1  ; GDT界限  
  32.         dd  0       ; GDT基地址  
  33.   
  34. ; GDT 选择子  
  35. SelectorNormal      equ LABEL_DESC_NORMAL   - LABEL_GDT  
  36. SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT  
  37. SelectorCode16      equ LABEL_DESC_CODE16   - LABEL_GDT  
  38. SelectorCodeDest    equ LABEL_DESC_CODE_DEST    - LABEL_GDT  
  39. SelectorCodeRing3   equ LABEL_DESC_CODE_RING3   - LABEL_GDT + SA_RPL3  
  40. SelectorData        equ LABEL_DESC_DATA     - LABEL_GDT  
  41. SelectorStack       equ LABEL_DESC_STACK    - LABEL_GDT  
  42. SelectorStack3      equ LABEL_DESC_STACK3   - LABEL_GDT + SA_RPL3  
  43. SelectorTSS     equ LABEL_DESC_TSS      - LABEL_GDT  
  44. SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT  
  45.   
  46. SelectorCallGateTest    equ LABEL_CALL_GATE_TEST    - LABEL_GDT + SA_RPL3  
  47. ; END of [SECTION .gdt]  
  48.   
  49. [SECTION .data1]     ; 数据段  
  50. ALIGN   32  
  51. [BITS   32]  
  52. LABEL_DATA:  
  53. SPValueInRealMode   dw  0  
  54. ; 字符串  
  55. PMMessage:      db  "In Protect Mode now. ^-^", 0   ; 进入保护模式后显示此字符串  
  56. OffsetPMMessage     equ PMMessage - $  
  57. StrTest:        db  "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0  
  58. OffsetStrTest       equ StrTest - $  
  59. DataLen         equ $ - LABEL_DATA  
  60. ; END of [SECTION .data1]  
  61.   
  62.   
  63. ; 全局堆栈段  
  64. [SECTION .gs]  
  65. ALIGN   32  
  66. [BITS   32]  
  67. LABEL_STACK:  
  68.     times 512 db 0  
  69. TopOfStack  equ $ - LABEL_STACK - 1  
  70. ; END of [SECTION .gs]  
  71.   
  72.   
  73. ; 堆栈段ring3  
  74. [SECTION .s3]  
  75. ALIGN   32  
  76. [BITS   32]  
  77. LABEL_STACK3:  
  78.     times 512 db 0  
  79. TopOfStack3 equ $ - LABEL_STACK3 - 1  
  80. ; END of [SECTION .s3]  
  81.   
  82.   
  83. ; TSS ---------------------------------------------------------------------------------------------  
  84. [SECTION .tss]  
  85. ALIGN   32  
  86. [BITS   32]  
  87. LABEL_TSS:  
  88.         DD  0           ; Back  
  89.         DD  TopOfStack      ; 0 级堆栈  
  90.         DD  SelectorStack       ;   
  91.         DD  0           ; 1 级堆栈  
  92.         DD  0           ;   
  93.         DD  0           ; 2 级堆栈  
  94.         DD  0           ;   
  95.         DD  0           ; CR3  
  96.         DD  0           ; EIP  
  97.         DD  0           ; EFLAGS  
  98.         DD  0           ; EAX  
  99.         DD  0           ; ECX  
  100.         DD  0           ; EDX  
  101.         DD  0           ; EBX  
  102.         DD  0           ; ESP  
  103.         DD  0           ; EBP  
  104.         DD  0           ; ESI  
  105.         DD  0           ; EDI  
  106.         DD  0           ; ES  
  107.         DD  0           ; CS  
  108.         DD  0           ; SS  
  109.         DD  0           ; DS  
  110.         DD  0           ; FS  
  111.         DD  0           ; GS  
  112.         DD  0           ; LDT  
  113.         DW  0           ; 调试陷阱标志  
  114.         DW  $ - LABEL_TSS + 2   ; I/O位图基址  
  115.         DB  0ffh            ; I/O位图结束标志  
  116. TSSLen      equ $ - LABEL_TSS  
  117. ; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
  118.   
  119.   
  120. [SECTION .s16]  
  121. [BITS   16]  
  122. LABEL_BEGIN:  
  123.     mov ax, cs  
  124.     mov ds, ax  
  125.     mov es, ax  
  126.     mov ss, ax  
  127.     mov sp, 0100h  
  128.   
  129.     mov [LABEL_GO_BACK_TO_REAL+3], ax  
  130.     mov [SPValueInRealMode], sp  
  131.   
  132.     ; 初始化 16 位代码段描述符  
  133.     mov ax, cs  
  134.     movzx   eax, ax  
  135.     shl eax, 4  
  136.     add eax, LABEL_SEG_CODE16  
  137.     mov word [LABEL_DESC_CODE16 + 2], ax  
  138.     shr eax, 16  
  139.     mov byte [LABEL_DESC_CODE16 + 4], al  
  140.     mov byte [LABEL_DESC_CODE16 + 7], ah  
  141.   
  142.     ; 初始化 32 位代码段描述符  
  143.     xor eax, eax  
  144.     mov ax, cs  
  145.     shl eax, 4  
  146.     add eax, LABEL_SEG_CODE32  
  147.     mov word [LABEL_DESC_CODE32 + 2], ax  
  148.     shr eax, 16  
  149.     mov byte [LABEL_DESC_CODE32 + 4], al  
  150.     mov byte [LABEL_DESC_CODE32 + 7], ah  
  151.   
  152.     ; 初始化测试调用门的代码段描述符  
  153.     xor eax, eax  
  154.     mov ax, cs  
  155.     shl eax, 4  
  156.     add eax, LABEL_SEG_CODE_DEST  
  157.     mov word [LABEL_DESC_CODE_DEST + 2], ax  
  158.     shr eax, 16  
  159.     mov byte [LABEL_DESC_CODE_DEST + 4], al  
  160.     mov byte [LABEL_DESC_CODE_DEST + 7], ah  
  161.   
  162.     ; 初始化数据段描述符  
  163.     xor eax, eax  
  164.     mov ax, ds  
  165.     shl eax, 4  
  166.     add eax, LABEL_DATA  
  167.     mov word [LABEL_DESC_DATA + 2], ax  
  168.     shr eax, 16  
  169.     mov byte [LABEL_DESC_DATA + 4], al  
  170.     mov byte [LABEL_DESC_DATA + 7], ah  
  171.   
  172.     ; 初始化堆栈段描述符  
  173.     xor eax, eax  
  174.     mov ax, ds  
  175.     shl eax, 4  
  176.     add eax, LABEL_STACK  
  177.     mov word [LABEL_DESC_STACK + 2], ax  
  178.     shr eax, 16  
  179.     mov byte [LABEL_DESC_STACK + 4], al  
  180.     mov byte [LABEL_DESC_STACK + 7], ah  
  181.   
  182.     ; 初始化堆栈段描述符(ring3)  
  183.     xor eax, eax  
  184.     mov ax, ds  
  185.     shl eax, 4  
  186.     add eax, LABEL_STACK3  
  187.     mov word [LABEL_DESC_STACK3 + 2], ax  
  188.     shr eax, 16  
  189.     mov byte [LABEL_DESC_STACK3 + 4], al  
  190.     mov byte [LABEL_DESC_STACK3 + 7], ah  
  191.   
  192.     ; 初始化Ring3描述符  
  193.     xor eax, eax  
  194.     mov ax, ds  
  195.     shl eax, 4  
  196.     add eax, LABEL_CODE_RING3  
  197.     mov word [LABEL_DESC_CODE_RING3 + 2], ax  
  198.     shr eax, 16  
  199.     mov byte [LABEL_DESC_CODE_RING3 + 4], al  
  200.     mov byte [LABEL_DESC_CODE_RING3 + 7], ah  
  201.   
  202.     ; 初始化 TSS 描述符  
  203.     xor eax, eax  
  204.     mov ax, ds  
  205.     shl eax, 4  
  206.     add eax, LABEL_TSS  
  207.     mov word [LABEL_DESC_TSS + 2], ax  
  208.     shr eax, 16  
  209.     mov byte [LABEL_DESC_TSS + 4], al  
  210.     mov byte [LABEL_DESC_TSS + 7], ah  
  211.   
  212.     ; 为加载 GDTR 作准备  
  213.     xor eax, eax  
  214.     mov ax, ds  
  215.     shl eax, 4  
  216.     add eax, LABEL_GDT      ; eax <- gdt 基地址  
  217.     mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址  
  218.   
  219.     ; 加载 GDTR  
  220.     lgdt    [GdtPtr]  
  221.   
  222.     ; 关中断  
  223.     cli  
  224.   
  225.     ; 打开地址线A20  
  226.     in  al, 92h  
  227.     or  al, 00000010b  
  228.     out 92h, al  
  229.   
  230.     ; 准备切换到保护模式  
  231.     mov eax, cr0  
  232.     or  eax, 1  
  233.     mov cr0, eax  
  234.   
  235.     ; 真正进入保护模式  
  236.     jmp dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处  
  237.   
  238. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  
  239.   
  240. LABEL_REAL_ENTRY:       ; 从保护模式跳回到实模式就到了这里  
  241.     mov ax, cs  
  242.     mov ds, ax  
  243.     mov es, ax  
  244.     mov ss, ax  
  245.   
  246.     mov sp, [SPValueInRealMode]  
  247.   
  248.     in  al, 92h     ; ┓  
  249.     and al, 11111101b   ; ┣ 关闭 A20 地址线  
  250.     out 92h, al     ; ┛  
  251.   
  252.     sti         ; 开中断  
  253.   
  254.     mov ax, 4c00h   ; ┓  
  255.     int 21h     ; ┛回到 DOS  
  256. ; END of [SECTION .s16]  
  257.   
  258.   
  259. [SECTION .s32]; 32 位代码段. 由实模式跳入.  
  260. [BITS   32]  
  261.   
  262. LABEL_SEG_CODE32:  
  263.     mov ax, SelectorData  
  264.     mov ds, ax          ; 数据段选择子  
  265.     mov ax, SelectorVideo  
  266.     mov gs, ax          ; 视频段选择子  
  267.   
  268.     mov ax, SelectorStack  
  269.     mov ss, ax          ; 堆栈段选择子  
  270.   
  271.     mov esp, TopOfStack  
  272.   
  273.   
  274.     ; 下面显示一个字符串  
  275.     mov ah, 0Ch         ; 0000: 黑底    1100: 红字  
  276.     xor esi, esi  
  277.     xor edi, edi  
  278.     mov esi, OffsetPMMessage    ; 源数据偏移  
  279.     mov edi, (80 * 10 + 0) * 2  ; 目的数据偏移。屏幕第 10 行, 第 0 列。  
  280.     cld  
  281. .1:  
  282.     lodsb  
  283.     test    al, al  
  284.     jz  .2  
  285.     mov [gs:edi], ax  
  286.     add edi, 2  
  287.     jmp .1  
  288. .2: ; 显示完毕  
  289.   
  290.     call    DispReturn  
  291.   
  292.     ; Load TSS  
  293.     mov ax, SelectorTSS  
  294.     ltr ax  ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。  
  295.   
  296.     push    SelectorStack3  
  297.     push    TopOfStack3  
  298.     push    SelectorCodeRing3  
  299.     push    0  
  300.     retf                ; Ring0 -> Ring3,历史性转移!将打印数字 '3'。  
  301.   
  302. ; ------------------------------------------------------------------------  
  303. DispReturn:  
  304.     push    eax  
  305.     push    ebx  
  306.     mov eax, edi  
  307.     mov bl, 160  
  308.     div bl  
  309.     and eax, 0FFh  
  310.     inc eax  
  311.     mov bl, 160  
  312.     mul bl  
  313.     mov edi, eax  
  314.     pop ebx  
  315.     pop eax  
  316.   
  317.     ret  
  318. ; DispReturn 结束---------------------------------------------------------  
  319.   
  320. SegCode32Len    equ $ - LABEL_SEG_CODE32  
  321. ; END of [SECTION .s32]  
  322.   
  323.   
  324. [SECTION .sdest]; 调用门目标段  
  325. [BITS   32]  
  326.   
  327. LABEL_SEG_CODE_DEST:  
  328.     mov ax, SelectorVideo  
  329.     mov gs, ax          ; 视频段选择子(目的)  
  330.   
  331.     mov edi, (80 * 12 + 0) * 2  ; 屏幕第 12 行, 第 0 列。  
  332.     mov ah, 0Ch         ; 0000: 黑底    1100: 红字  
  333.     mov al, 'C'  
  334.     mov [gs:edi], ax  
  335.   
  336.     retf  
  337. SegCodeDestLen  equ $ - LABEL_SEG_CODE_DEST  
  338. ; END of [SECTION .sdest]  
  339.   
  340.   
  341. ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式  
  342. [SECTION .s16code]  
  343. ALIGN   32  
  344. [BITS   16]  
  345. LABEL_SEG_CODE16:  
  346.     ; 跳回实模式:  
  347.     mov ax, SelectorNormal  
  348.     mov ds, ax  
  349.     mov es, ax  
  350.     mov fs, ax  
  351.     mov gs, ax  
  352.     mov ss, ax  
  353.   
  354.     mov eax, cr0  
  355.     and al, 11111110b  
  356.     mov cr0, eax  
  357.   
  358. LABEL_GO_BACK_TO_REAL:  
  359.     jmp 0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值  
  360.   
  361. Code16Len   equ $ - LABEL_SEG_CODE16  
  362.   
  363. ; END of [SECTION .s16code]  
  364.   
  365. ; CodeRing3  
  366. [SECTION .ring3]  
  367. ALIGN   32  
  368. [BITS   32]  
  369. LABEL_CODE_RING3:  
  370.     mov ax, SelectorVideo  
  371.     mov gs, ax          ; 视频段选择子(目的)  
  372.   
  373.     mov edi, (80 * 14 + 0) * 2  ; 屏幕第 14 行, 第 0 列。  
  374.     mov ah, 0Ch         ; 0000: 黑底    1100: 红字  
  375.     mov al, '3'  
  376.     mov [gs:edi], ax  
  377.   
  378.     call    SelectorCallGateTest:0  ; 测试调用门(有特权级变换),将打印字母 'C'。  
  379.     jmp $  
  380. SegCodeRing3Len equ $ - LABEL_CODE_RING3  
  381. ; END of [SECTION .ring3]  
; ==========================================
; pmtest5.asm
; 编译方法:nasm pmtest5.asm -o pmtest5.com
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些说明

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		; 非一致代码段, 32
LABEL_DESC_CODE16:	Descriptor	       0,              0ffffh, DA_C			; 非一致代码段, 16
LABEL_DESC_CODE_DEST:	Descriptor	       0,  SegCodeDestLen - 1, DA_C + DA_32		; 非一致代码段, 32
LABEL_DESC_CODE_RING3:	Descriptor	       0, SegCodeRing3Len - 1, DA_C + DA_32 + DA_DPL3	; 非一致代码段, 32
LABEL_DESC_DATA:	Descriptor	       0,	  DataLen - 1, DA_DRW			; Data
LABEL_DESC_STACK:	Descriptor	       0,          TopOfStack, DA_DRWA + DA_32		; Stack, 32 位
LABEL_DESC_STACK3:	Descriptor	       0,         TopOfStack3, DA_DRWA + DA_32 + DA_DPL3; Stack, 32 位
LABEL_DESC_TSS:		Descriptor	       0,          TSSLen - 1, DA_386TSS		; TSS
LABEL_DESC_VIDEO:	Descriptor	 0B8000h,              0ffffh, DA_DRW + DA_DPL3		; 显存首地址

; 门                                            目标选择子,       偏移, DCount, 属性
LABEL_CALL_GATE_TEST:	Gate		  SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3
; GDT 结束

GdtLen		equ	$ - LABEL_GDT	; GDT长度
GdtPtr		dw	GdtLen - 1	; GDT界限
		dd	0		; GDT基地址

; GDT 选择子
SelectorNormal		equ	LABEL_DESC_NORMAL	- LABEL_GDT
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorCode16		equ	LABEL_DESC_CODE16	- LABEL_GDT
SelectorCodeDest	equ	LABEL_DESC_CODE_DEST	- LABEL_GDT
SelectorCodeRing3	equ	LABEL_DESC_CODE_RING3	- LABEL_GDT + SA_RPL3
SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT
SelectorStack		equ	LABEL_DESC_STACK	- LABEL_GDT
SelectorStack3		equ	LABEL_DESC_STACK3	- LABEL_GDT + SA_RPL3
SelectorTSS		equ	LABEL_DESC_TSS		- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT

SelectorCallGateTest	equ	LABEL_CALL_GATE_TEST	- 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]


; TSS ---------------------------------------------------------------------------------------------
[SECTION .tss]
ALIGN	32
[BITS	32]
LABEL_TSS:
		DD	0			; Back
		DD	TopOfStack		; 0 级堆栈
		DD	SelectorStack		; 
		DD	0			; 1 级堆栈
		DD	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
		DD	0			; ES
		DD	0			; CS
		DD	0			; SS
		DD	0			; DS
		DD	0			; FS
		DD	0			; GS
		DD	0			; LDT
		DW	0			; 调试陷阱标志
		DW	$ - LABEL_TSS + 2	; I/O位图基址
		DB	0ffh			; I/O位图结束标志
TSSLen		equ	$ - LABEL_TSS
; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


[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 位代码段描述符
	mov	ax, cs
	movzx	eax, ax
	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, 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

	; 初始化数据段描述符
	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

	; 初始化堆栈段描述符(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

	; 初始化 TSS 描述符
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_TSS
	mov	word [LABEL_DESC_TSS + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_TSS + 4], al
	mov	byte [LABEL_DESC_TSS + 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, 并跳转到 Code32Selector:0  处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

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]; 32 位代码段. 由实模式跳入.
[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 + 0) * 2	; 目的数据偏移。屏幕第 10 行, 第 0 列。
	cld
.1:
	lodsb
	test	al, al
	jz	.2
	mov	[gs:edi], ax
	add	edi, 2
	jmp	.1
.2:	; 显示完毕

	call	DispReturn

	; Load TSS
	mov	ax, SelectorTSS
	ltr	ax	; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。

	push	SelectorStack3
	push	TopOfStack3
	push	SelectorCodeRing3
	push	0
	retf				; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

; ------------------------------------------------------------------------
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
; DispReturn 结束---------------------------------------------------------

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


[SECTION .sdest]; 调用门目标段
[BITS	32]

LABEL_SEG_CODE_DEST:
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 12 + 0) * 2	; 屏幕第 12 行, 第 0 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'C'
	mov	[gs:edi], ax

	retf
SegCodeDestLen	equ	$ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]


; 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]

; CodeRing3
[SECTION .ring3]
ALIGN	32
[BITS	32]
LABEL_CODE_RING3:
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 14 + 0) * 2	; 屏幕第 14 行, 第 0 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, '3'
	mov	[gs:edi], ax

	call	SelectorCallGateTest:0	; 测试调用门(有特权级变换),将打印字母 'C'。
	jmp	$
SegCodeRing3Len	equ	$ - LABEL_CODE_RING3
; END of [SECTION .ring3]

 

83—117初始化任务状态堆栈段(TSS) 292—300 通过使用retf完成从Ring0-->Ring3的跳转,即高特权级跳转到低特权级,跳转到LABEL_CODE_RING3。 380—380 通过调用门,完成从Ring3-->Ring0的跳转,即低特权级跳转到高特权级,跳转到LABEL_SEG_CODE_DEST 336—336 通过retf,完成从Ring0-->Ring3的跳转,即高特权级跳转到低特权级,跳转到379。 到目前为止,我们已经实现了两次从高特权级到低特权级以及一次从低特权级到高特权级的转移,最终在低特权级的代码中让程序停住。

你可能感兴趣的:(操作系统)