# 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