OS_lab——Protected mode GDT, Descriptor, Selector, GDTR and the relationship between them

1.  Related data structures of protected mode

Definition of necessary data structures for protected mode

• GDT: Global  Descriptor  Table , also known as segment descriptor table, is a data structure in protected mode. It contains multiple describe tors, which define the starting , boundary attributes, etc. 

• Descriptor: Segment descriptor, including segment base address, segment boundary, and segment attributes.

• Selector: Selector, its function is to indicate the offset of the segment descriptor relative to the GDT base address.

•    GDTR : GDT  register. Its structure is similar to  GDTPTR  , with  6 bytes. The first two bytes are  the GDT limit, and the last  4  bytes are the  GDT  base address.

Definition of macro descriptor

As shown in the figure below, Descriptor  stores segment base addresses, segment boundaries and related attributes respectively. For some reasons,  segment base addresses and segment boundaries are stored separately:

2.  From real mode to protected mode

pmtest 1. asm code display

; ==========================================

; pmtest1.asm

; 编译方法:nasm pmtest1.asm -o pmtest1.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,

; 并跳转到 Code32Selector:0  处

; END of [SECTION .s16]





[SECTION .s32]; 32 位代码段. 由实模式跳入.

[BITS   32]



LABEL_SEG_CODE32:

mov ax, SelectorVideo

mov g s, ax           ; 视频段选择子(目的)



mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。

mov ah, 0Ch                  ; 0000: 黑底    1100: 红字

mov al, 'P'

mov [gs:edi], ax



; 到此停止

jmp $



SegCode32Len    equ $ - LABEL_SEG_CODE32

; END of [SECTION .s32]

Results of the

From real mode to protected mode

1.   Prepare GDT.

2.  Load gdtr with lgdt  . 

3.   Open  A20.

4. Set the  PE bit of cr0  .  

5. Jump and enter protected mode.

jmp jump: dword prefix

In line  71 of pmtest1.asm  : jmp dword SelectorCode32 :0 Deleting the dword identifier will cause the offset address to be truncated after compilation, and may not jump to the specified location correctly. Reason: At this time, the operating environment of the operating system has been converted from real mode (16-bit) to protected mode (32- bit ). However, due to compiler limitations, this code can only be placed in the 16 -bit assembly code part, so a dword is required. Flag to prevent 32 - bit length offset address from being truncated and causing unknown errors. The decompiled source code of the test code is as follows:                  

GDT table structure

 Structure of Descriptor

The construction of the segment descriptor is carried out with the help of the  pm.inc  file, which defines the segment base address, segment boundary and related attributes respectively  . The macro will reorganize the data according to the definition to meet the requirements of the specification.

Selector structure

The value of the Selector is the difference between  the segment descriptor position and  the initial position of the GDT table. 

Switching GDT table

Since the code in real mode stores the offset address of the segment in  the segment base address of the GDT table.  Therefore, in protected mode , the program can use the predefined Selector to switch the GDT table.      

3.2.  Return to real mode from protected mode

It is opposite to the method of entering protected mode from real mode, but since the related operations of entering real mode need to be written in the  16  -bit code segment, and due to cache reasons, there are several steps :

1. Load the relevant segment register.

2.   Clear the  PE bit of cr0  . 

3. Jump and return to the  16-  bit code segment.

4. Set the real mode segment register.

5.   Close  A20.

6.   Turn on interrupt

7. Return to real mode

Execution effect display

The picture below shows the execution effect of the  pmtest2.asm  code. It can be seen that after booting into protected mode, the program  prints characters and returns to  the DOS  system in real mode:

4.  LDT  switching

LDT (Local Descriptor Table)

LDT is the same  as  GDT  , both are descriptor tables, but  the LDT  table only has a partial scope, while the  GDT  table has  a global scope.

pmtest 3. asm code snippet display

[SECTION .gdt]

...

LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT     ;

LDT

...

SelectorLDT     equ LABEL_DESC_LDT      - LABEL_GDT

; END of [SECTION .gdt]

...

[SECTION .s32]; 32 位代码段. 由实模式跳入.

[BITS   32]

LABEL_SEG_CODE32:

...

; Load LDT

mov ax, SelectorLDT

lldt    ax

jmp SelectorLDTCodeA:0  ; 跳入局部任务

...

; END of [SECTION .s32]

...

; LDT

[SECTION .ldt]

ALIGN   32

LABEL_LDT:

;                             段基址       段界限      属性

LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位

LDTLen      equ $ - LABEL_LDT

; LDT 选择子

SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL

; END of [SECTION .ldt]

Structure of LDT table

1.  Define the code segment corresponding to the LDT table in the GDT table   

2.  Define the corresponding LDT table in the LDT table   

– In the selector definition of the  LDT  table, based on the code offset,  an additional attribute  SA_TIL needs to be added. This attribute will be stored in  the TL part of the selector in the pm, inc macro  to identify whether the selector is Selector of LDT table     

Use of LDT table

1. Use the  lldt  command to load the segment where  the LDT  table is located

2. Use  the selector corresponding to the corresponding LDT table to jump 

3.   Execute code

Execution effect display

The picture below  shows the execution effect of the pmtest3.asm code. As you can see, the program  prints out the letter L in the LDT table section:   

5. Permission access and switching between segments

Permission access rules

In  the IA32  segmentation mechanism, there are a total of  4  privilege levels , from high to low, which are  0, 1, 2, and 3. The smaller the number, the greater the privilege level.

CPL

CPL  is the privilege level of the currently executing program or task. It is stored in  bits 0 and 1 of cs  and  ss  . Under normal circumstances, CPL is equal to the privilege level of the segment where the code is located. The processor will change the CPL when the program moves to a code segment with a different privilege level . Consistent code segments can be accessed by code with the same or lower privilege level . When the processor accesses a consistent code segment with a different privilege level than the CPL , the CPL is not changed .            

DPL

DPL  represents the privilege level of a segment or gate.  It is stored in the DPL field  of the segment descriptor or gate descriptor .  When the current code segment attempts to access a segment or gate, the DPL  will  be compared with the CPL and  the RPL of the segment or gate selector . The DPL  will be treated differently depending on the segment or gate type:  

• Data segment: DPL  specifies the minimum privilege level that can access this segment .

• Non-conforming code section (without using call gates): DPL  specifies the privilege level for accessing this section .

• Conforming code section: DPL  specifies the highest privilege level for accessing this section .

RPL

RPL is  expressed through bits 0 and 1 of the segment selector .  The processor checks the RPL and CPL to  confirm whether an access request is legal. That is to say, if the number of RPL is larger than that of CPL (the larger the number, the lower the privilege level), then RPL will play a decisive role, and vice versa.            

Privilege level verification test

Use  pmtest2.asm to modify  the DPL of the data segment descriptor and  the RPL of its segment selector  :  

[SECTION .gdt]

; GDT

;

LABEL_DESC_DATA:   Descriptor 0, DataLen-1, DA_DRW+DA_DPL1 ; Data

; GDT 结束



; GDT 选择子

SelectorData   equ LABEL_DESC_DATA - LABEL_GDT + SA_RPL3

; END of [SECTION .gdt]

The debugging results are as follows:

Debugger error: load_seg_reg  (DS, 0x0023): RPL & CPL must be <= DPL proves that the rules of privilege level management are violated  here

Switch between segments

There are several ways to transfer between different code segments:

   Use  jmp  or  call  instructions

  The target operand contains the segment selector of the target code segment

– The target operand points to a call gate descriptor containing the target code section selector.

• Transfer using gate descriptors

Among them, the transfer between code segments that can be performed through  jmp  and  call  is very limited. For non-consistent code  segments, it can only be transferred between code segments with the same privilege level. When encountering a consistent code segment, it can go from low to high at most,  and the CPL  will not change. If you need to transfer between different privilege levels, you need to use the gate descriptor  to transfer immediately.

gate descriptor

Gate descriptor structure

The call gate directly defines a series of attributes such as the selector and entry offset address of the corresponding segment of the target code , and can  directly jump to the code segment.  There are four types of door descriptors  : 

   call gate

   Interrupt gate

   Trap door

   Task gate

Use of call gates

pmtest4.asm  code snippet display

[SECTION .gdt]

; GDT

;                            段基址,       段界限     , 属性

LABEL_DESC_CODE_DEST: Descriptor 0,SegCodeDestLen-1, DA_C+DA_32; 非一致 代码段,32

...

; 门                               目标选择子,偏移,DCount, 属性

LABEL_CALL_GATE_TEST:  Gate  SelectorCodeDest,       0,            0,

DA_386CGate+DA_DPL0

...

; GDT 选择子

SelectorCodeDest    equ LABEL_DESC_CODE_DEST    - LABEL_GDT



SelectorCallGateTest    equ LABEL_CALL_GATE_TEST    - LABEL_GDT

; END of [SECTION .gdt]

...

[SECTION .s32]; 32 位代码段. 由实模式跳入.

[BITS   32]

...

; 测试调用门(无特权级变换),将打印字母 'C'

call    SelectorCallGateTest:0

...

jmp SelectorLDTCodeA:0  ; 跳入局部任务,将打印字母 'L'。

...

; END of [SECTION .s32]

Use  the Gate macro in pm . inc  to call the gate. In the code snippet, the location pointed to by this gate is SelectorCodeDest:0, which is the code at LABEL_DESC_CODE_DEST .     

Execution effect display

The program enters the code section pointed to by the call gate and outputs the letter  C

6.  Use call gates to change privilege levels

Privilege checks used by call gates

Privilege level translation of the stack - TSS

Because when making  a call-ret jump, the stack will store  the cs:eip address before the jump  and return at the end of . However, when there is a privileged code jump, the stack segment called will also change. Therefore, TSS (Tasj-State Stack) needs to be used to avoid such problems.    

 Structure of TSS

 Transfer process assisted by TSS 

 1. Select which ss and esp should be switched to from the TSS  based on the DPL (new CPL) of the target code segment   

2.  Read new ss and esp from TSS  . During this process, if ss, esp or TSS boundary errors are found, an invalid TSS exception (#TS) will occur .          

3. Check  the ss  descriptor. If an error occurs, a #TS  exception will also be generated. Also produces #TS  open  m. mt  access media equipment

4. Temporarily save the current  ss  and  esp  values.

5. Load new  ss  and  esp.

6. Push the just saved  ss  and  esp  values ​​into the new stack.

7. Copy the parameters from the caller stack to the callee stack (new stack). The number of copied parameters  is determined by the Param Count item in the call gate .  If  Param Count  is 0  , parameters  will not be copied. 

8. Push the current  cs  and  eip  onto the stack.

9. Load the new  cs  and  eip specified in the call gate and start executing the callee process. 

  TSS-  assisted return process

 1. Check the RPL in the saved cs  to determine whether to change the privilege level when returning.  

 2. Load the cs  and  eip  on the callee stack (at this time, the code segment descriptor and selected subtype  and privilege level will be checked).

3. If  the ret  instruction contains parameters, increase the value  of esp to skip the parameters, and then esp will point to the saved caller ss and esp . Note that the parameter of ret  must correspond to the value of ParamCount in the call gate .              

4. Load  ss  and  esp, switch to the caller stack, and the callee's  ss  and  esp  are discarded.  Here  the ss  descriptor, esp  and  ss  segment descriptor will be checked.

5. If  the ret  instruction contains parameters, increase the value of  esp  to skip the parameters (which are already on the caller stack  ).

6. Check the values ​​of  ds , es , fs , and gs . If  the DPL of the segment pointed to by any of the registers  is less than  the CPL (this rule does not apply to consistent code segments), then an empty descriptor will be loaded into the register .  

Using call gates for privilege level translation

pmtest5.asm  implements a  ring 3  privilege level code (which will print the number  3 on the screen ); it also  initializes TSS and prints the letter  C on the screen by referencing  the ring 0 privilege level code fragment through the call gate.  The experimental results are shown in the figure below:  

Test success

1. Problem solving and hands-on modification

1.1 GDT , Descriptor , Selector , GDTR  structures, and what are their meanings? How are they  related ?  How to use the macros defined by pm.inc ?

       GDT (Global  Descriptor Table  ), also called segment descriptor table, is a data structure in protected  mode . It contains multiple  descriptors, which define the starting address of the segment, boundary  attributes, etc., and its function is to provide a segmented storage mechanism;

Descriptor  is a segment descriptor, including segment base address, segment limit, and segment attributes;

       Selector  is a selector and can correspond to a descriptor. In  the pmtest1.asm  program , it corresponds  to the offset of the descriptor relative to  the GDT  base address.

       GDT  is a structure array that contains multiple  Descriptors. Each  Descriptor  is  an entry in the GDT  array and stores the segment base address, segment boundaries and attributes of each segment.  The Selector  records  the offset, table type corresponding Descriptor  relative to the  GDT base address (LABEL_GDT).  GDTR records the base address and limits of the GDT table. Based on the above, you can get  the address of a Descriptor . Addressing in protected mode is to first find the GDT by GDTR , then find the address of the corresponding segment according to the Descriptor , and then add the offset within the segment to obtain a linear address.           

The Descriptor in the program is generated  by the macro  Descriptor in pm.inc  . The specific use of macros is as follows:   

a.  Macro name  Descriptor, 3  indicates that there are three parameters, namely segment base address, limit, and attribute;

b.  The first line  of dw  is two bytes, which determines the segment boundary;

c.  The second and third lines  dw  and  dd  determine the segment base addresses  1 and 2;

d. The two bytes  of dw in the fourth line  constitute the segment attribute and segment boundary 2;  

e. The two bytes of  dw in the last line  constitute segment base address 3.  

1.2   What are the key steps from real mode to protected mode? Why turn off interrupts? Why open  the A20  address line? What steps are needed to switch from protected mode back to real mode?

Steps from real mode to protected mode:

a. Prepare  GDT, initialize  GDT  attributes, and define the descriptors and selectors of the required segments; 

b. Initialization section;

c.  Fill the physical address of GDT into  GdtPtr  , and then load the filled address into register gdtr;  

d.  Turn off interrupt;

e. Open  the A20  address line;

f. Set the  PE bit of cr0  ;  

g. Jump to the first address of the segment corresponding to the descriptor and enter protected mode.

       The reason why interrupts need to be turned off is that the interrupt processing is different from that in real mode. If  interrupts are enabled but the corresponding interrupt processing mechanism is not yet complete, an error will occur.

 The reason why the A20 address line         needs to be opened is that  there is only a 20 -bit address bus in the 8086 CPU  , and its maximum addressing capability can only reach 1 MB. The 8086 design is that when a program accesses a memory address above 1 MB , it will " wrap back " from address 0. That is to say, when accessing 1 MB and zero 1 bit, the actual space accessed is address 1 . In later CPUs , the access space has already exceeded 1 MB, which leads to incompatibility. IBM designed that if A20 is not opened, the rollback mechanism will continue to be used, and the twentieth address will be 0. If you open A2 0, you can access memory addresses above 1 MB normally.                            

Steps from real mode to protected mode:

  1. Load an appropriate descriptor selector into the relevant segment register;
  2. Jump to  16  -bit code segment;
  3. Clear  the PE bit of cr0  ;  
  4. Jump back to real mode;
  5. Close  A20;
  6. Turn on interrupts.

1.3   Explain the switching principle of different permission codes. What are the usage scenarios of call,  jmp and retf  ? Can they be interchanged?

       For  jmp  , long jumps and short jumps only have different results. Short jumps correspond to intra-segment  jumps , and long jumps correspond to inter-segment jumps.

       For  call  , since  the call  instruction will affect the stack , long calls and short calls will have different  effects. In the short call, the call  instruction will push the next instruction  eip  onto the stack. When  the ret  instruction is executed  , the  eip  will is popped from the stack.

       During a long call, the call  instruction will also push  eip  and  cs  onto the stack. When  retf is encountered,  eip and cs will pop up  . Generally speaking, call is equivalent to push+jmp and ret is equivalent to pop+jmp.       

1.4   Hands-on modification:

 Customize and add  1  GDT code segment and 1 LDT code segment. In the GDT segment, a  string is written to a memory data structure , and then the function of the code segment in the LDT segment is to read and print the content of the GDT ;          

This function is implemented with reference to  pmtest3.asm  . The experimental results are as follows:

Step 1:  Modify  the StrTest content in the .Data section

Step 2: Modify the  32-  flavor code segment and jump into the  LDT  local task

Step 3: Modify  the logic in LDT and CodeA , set the offset to  the offset of TestStr , and finally output it to the screen   

 Customize  two  GDT code segments A and B, which  belong to different privilege levels. The functions are customized and require the jump from A to B and the jump from B to A.      

Guess you like

Origin blog.csdn.net/weixin_49816179/article/details/135433928