GDT加載過程分析

# Bootstrap GDT
.p2align 2                                # force 4 byte alignment 向後移動位置計數器置爲4字節的倍數 爲了內存對齊
gdt:
  SEG_NULLASM                             # null seg
  SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)   # code seg 可執行段或可讀可執行段
  SEG_ASM(STA_W, 0x0, 0xffffffff)         # data seg 可寫但是不可執行段

gdtdesc:
  .word   (gdtdesc - gdt - 1)             # sizeof(gdt) - 1 gdt表的長度,以字節爲單位 這裏不太懂爲什麼這樣寫
  .long   gdt                             # address gdt .long後面的參數爲gdt運行時生成的值,即gdt表的地址

在x86中地址爲32位即4字節,.p2align 2 就是將位置計數器移動到4字節的倍數,如果已經是4字節的倍數則不會發生變化。全局描述符號的第一段爲空段,這是intel的規定。後兩個段是數據段和代碼段,最大限長爲4GB。下圖爲GDT的結構。

段描述符的定義:

struct segdesc {
  uint lim_15_0 : 16;  // Low bits of segment limit
  uint base_15_0 : 16; // Low bits of segment base address
  uint base_23_16 : 8; // Middle bits of segment base address
  uint type : 4;       // Segment type (see STS_ constants)
  uint s : 1;          // 0 = system, 1 = application
  uint dpl : 2;        // Descriptor Privilege Level
  uint p : 1;          // Present
  uint lim_19_16 : 4;  // High bits of segment limit
  uint avl : 1;        // Unused (available for software use)
  uint rsv1 : 1;       // Reserved
  uint db : 1;         // 0 = 16-bit segment, 1 = 32-bit segment
  uint g : 1;          // Granularity: limit scaled by 4K when set
  uint base_31_24 : 8; // High bits of segment base address
 };

在整個系統中GDT只有一張,GDT可以被放在內存的任何位置,但CPU必須知道GDT的入口,也就是基地址放在哪裏,Intel的設計者門提供了一個寄存器GDTR用來存放GDT的入口地址,程序員將GDT設定在內存中某個位置之後,可以通過LGDT指令將GDT的入口地址裝入此寄存器,從此以後,CPU就根據此寄存器中的內容作爲GDT的入口來訪問GDT了。GDTR中存放的是GDT在內存中的基地址和其表長界限。

關於SEG_ASM的定義在ASM.h中

//
// assembler macros to create x86 segments
//
//定義了一個空段描述符
//.word表示就地生成一個字大小的數,.byte就地生成一個字節的數
#define SEG_NULLASM                                             \
        .word 0, 0;                                             \
        .byte 0, 0, 0, 0

//以type,base,lim爲參數定義一個段描述符,0xC0=11000000,其中
//第一個1對應於段描述符中的G位,置1表示段界限以4KB爲單位
//第二個1對應於段描述符中的D位,置1表示是保護模式下的段描述符
//關於段描述符的格式定義在mmu.h中
// The 0xC0 means the limit is in 4096-byte units
// and (for executable segments) 32-bit mode.
#define SEG_ASM(type,base,lim)                                \
        .word (((lim) >> 12) & 0xffff), ((base) & 0xffff);      \
        .byte (((base) >> 16) & 0xff), (0x90 | (type)),         \
                (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)

#define STA_X     0x8       // Executable segment可執行段
#define STA_E     0x4       // Expand down (non-executable segments)非可執行段
#define STA_C     0x4       // Conforming code segment (executable only)只能執行的段
#define STA_W     0x2       // Writeable (non-executable segments)可寫但是不可執行的段
#define STA_R     0x2       // Readable (executable segments)可讀可執行的段
#define STA_A     0x1       // Accessed表明描述符是否已被訪問,把選擇字裝入段寄存器時,標記爲1

關於這段代碼,lgdt指令是將GDT入口地址存到gdtdesc寄存器裏,下面三行是修改cr0寄存器的值。gdt在內存中的位置在定義的時候就確定了。

lgdt    gdtdesc
  movl    %cr0, %eax
  orl     $CR0_PE, %eax
  movl    %eax, %cr0

AT&T:https://blog.csdn.net/nancygreen/article/details/14445829

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章