LInux 描述符GDT, IDT & LDT结构定义

说明:此博文是Kernel有关GDT,LDT以及LDT的相关源码剖析,而对有关GDT,LDT以及IDT的详细说明以及原理讲解都可以在Intel IA-32架构手册中查到!这里不再赘述,(手册地址)

1、描述符(64bits)分类

2、三者的关系

a、GDT,IDT都是全局的。LDT是局部的(在GDT中有它的描述符)

b、GDT用来存储描述符(门或非门);系统中几个CPU,就有几个GDT

struct gdt_page {
    struct desc_struct gdt[GDT_ENTRIES];
} __attribute__((aligned(PAGE_SIZE)));

DECLARE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page);

c、IDT整个系统只有一个

d、系统启动时候需要初始化GDT和IDT。LDT和进程相关,并不一定必有

 

3、IA-32各种描述符的结构


4、描述符结构定义

<arch/x86/include/asm/desc_defs.h>

struct desc_struct {
    union {
        struct {
            unsigned int a;
            unsigned int b;
        };
        struct {
            u16 limit0;
            u16 base0;
            unsigned base1: 8, type: 4, s: 1, dpl: 2, p: 1;
            unsigned limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8;
        };
    };  
} __attribute__((packed));

联合体——对成员域访问和设置成为一种很优美的方法。上面第一个匿名结构体用来作为成员访问取值的出口,下面第二个结构体对真实的成员设置值的入口。

字段(结合上面的图示):

limit:段长度

base:段的首字节的线性地址,有base0,base1,base2三部分构成

type:段的类型和存取权限

s:系统标志。1-系统段;0-普通段

dpl:描述符特权级

p:segment-Present。linux下总是1

avl:linux不用

d:区分代码段还是数据段

g:段大小粒度。以4K倍数计算

在32位机器上,这就是所有描述符的数据结构喽,没有细分门和非门!

typedef struct desc_struct gate_desc;
typedef struct desc_struct ldt_desc;
typedef struct desc_struct tss_desc;

由于三类描述符都是一个结构类型,从而一律使用下面宏初始化在GDT中表项

#define GDT_ENTRY_INIT(flags, base, limit) { { { \
        .a = ((limit) & 0xffff) | (((base) & 0xffff) << 16), \
        .b = (((base) & 0xff0000) >> 16) | (((flags) & 0xf0ff) << 8) | \
            ((limit) & 0xf0000) | ((base) & 0xff000000), \
    } } }

但是在64位机器上,Linux则进行了细致划分:

16字节门描述符结构

/* 16byte gate */
struct gate_struct64 {
    u16 offset_low;
    u16 segment;
    unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
    u16 offset_middle;
    u32 offset_high;
    u32 zero1;
} __attribute__((packed));

16字节LDT或TSS描述符结构

/* LDT or TSS descriptor in the GDT. 16 bytes. */
struct ldttss_desc64 {
    u16 limit0;
    u16 base0;
    unsigned base1 : 8, type : 5, dpl : 2, p : 1;
    unsigned limit1 : 4, zero0 : 3, g : 1, base2 : 8;
    u32 base3;
    u32 zero1;
} __attribute__((packed));


typedef struct gate_struct64 gate_desc;
typedef struct ldttss_desc64 ldt_desc;
typedef struct ldttss_desc64 tss_desc;

从上面代码看出无论是32位还是64位机器上,都使用typedef重新定义,以提供给系统其他使用此描述符的部分一致的类型名

区分描述符的枚举量

enum {
    GATE_INTERRUPT = 0xE,
    GATE_TRAP = 0xF,
    GATE_CALL = 0xC,
    GATE_TASK = 0x5,
};
enum {
    DESC_TSS = 0x9,
    DESC_LDT = 0x2,
    DESCTYPE_S = 0x10,  /* !system */
};

系统GDT,IDT指针描述结构

struct desc_ptr {
    unsigned short size;
    unsigned long address;
} __attribute__((packed)) ;
这个结构记录了系统的GDT或者IDT的大小以及在系统中的线性基地

Reference:

<arch/x86/include/asm/desc_defs.h>

http://www.osdever.net/tutorials/pdf/descriptors.pdf。 



  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要了解一下全局描述符表(Global Descriptor Table,GDT)是什么。GDT是在操作系统启动时由CPU初始化的一个数据结构,用于管理内存段的访问权限。操作系统可以通过修改GDT来控制内存的访问权限和保护。 在Linux系统中,GDT的初始化是在setup.s文件中完成的。下面是setup.s文件中初始化GDT的代码和注释: ``` /* * 初始化GDT */ init_gdt: lgdt gdtr /* 加载GDT寄存器 */ /* GDT表项0:空描述符 */ gdt_null: .word 0, 0 .byte 0, 0, 0, 0 /* GDT表项1:内核代码段描述符 */ gdt_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为0,系统段,粒度为4KB */ /* GDT表项2:内核数据段描述符 */ gdt_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为0,系统段,粒度为4KB */ /* GDT表项3:用户代码段描述符 */ gdt_user_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为3,用户段,粒度为4KB */ /* GDT表项4:用户数据段描述符 */ gdt_user_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为3,用户段,粒度为4KB */ /* GDT表项5:TSS描述符 */ gdt_tss: .word 0x67, 0 /* 段界限 */ .byte 0, 0, 0x89, 0x40 /* 基地址为tss_entry,DPL为0,系统段,粒度为1B */ .word 0 /* tss_entry的低16位 */ .byte 0 /* tss_entry的第24位 */ .byte 0x00 /* tss_entry的第32位 */ .word 0 /* tss_entry的高16位 */ /* GDT表项6:LDT描述符 */ gdt_ldt: .word 0, 0 /* 段界限 */ .byte 0, 0, 0x82, 0x40 /* 基地址为ldt,DPL为0,系统段,粒度为1B */ /* GDT表项7:TSS段 */ gdt_tss_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项8:LDT段 */ gdt_ldt_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项9:TSS段 */ gdt_tss2_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项10:LDT段 */ gdt_ldt2_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项11:TSS段 */ gdt_tss3_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项12:LDT段 */ gdt_ldt3_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ .fill 16 * 8 - (. - init_gdt) /* 填充到16字节 */ /* GDT表 */ gdt: .word 16 * 8 - 1 /* GDT表长度 */ .long gdt /* GDT表地址 */ /* GDT寄存器 */ gdtr: .word 16 * 8 - 1 /* GDT表长度 */ .long gdt /* GDT表地址 */ ``` 上述代码中,首先使用`lgdt`指令将GDT表的长度和地址加载到GDT寄存器中。然后,定义了一些GDT表项,包括空描述符内核代码段描述符内核数据段描述符、用户代码段描述符、用户数据段描述符、TSS描述符LDT描述符。其中,每个GDT表项包括段界限、基地址、段属性等信息。 最后,定义了一个GDT表数组`gdt`,将GDT表长度和地址存储在`gdtr`寄存器中,完成了GDT的初始化。 需要注意的是,上述代码只是一个简化的示例,实际的GDT表项可能更加复杂,具体的实现方式也可能因操作系统的不同而有所差异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值