2

我正在用 c 为 x86 平台编写一个小内核,但是我在加载 gdt 和重新加载段选择器时遇到了麻烦。

我正在使用 bochs 来测试我的内核。

问题是,当我加载 GDT 但不重新加载段选择器时,我可以停止我的程序,输入info gdt并获得一个不错的结果: 当我不加载我的 GDT 时:

<bochs:2> info gdt
Global Descriptor Table (base=0x00000000000010b0, limit=32):
GDT[0x0000]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0008]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0010]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, Accessed, 32-bit
GDT[0x0018]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
<bochs:3> 

当我加载我的 GDT 时:

<bochs:2> info gdt
Global Descriptor Table (base=0x00000000001022a0, limit=48):
GDT[0x0000]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0008]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, 32-bit
GDT[0x0010]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write
GDT[0x0018]=Code segment, base=0x00000000, limit=0x00000fff, Execute-Only, Non-Conforming, 32-bit
GDT[0x0020]=Data segment, base=0x00000000, limit=0x00000fff, Read-Only
GDT[0x0028]=??? descriptor hi=0x00000000, lo=0x00000000
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
<bochs:3> 

所以看来我的 GDT 已正确加载。

现在是棘手的部分。当我想重新加载段选择器时,我遇到了这个错误:

04641352650e[CPU0  ] fetch_raw_descriptor: GDT: index (ff57) 1fea > limit (30)
04641352650e[CPU0  ] interrupt(): vector must be within IDT table limits, IDT.limit = 0x0
04641352650e[CPU0  ] interrupt(): vector must be within IDT table limits, IDT.limit = 0x0
04641352650i[CPU0  ] CPU is in protected mode (active)
04641352650i[CPU0  ] CS.mode = 32 bit
04641352650i[CPU0  ] SS.mode = 32 bit
04641352650i[CPU0  ] EFER   = 0x00000000
04641352650i[CPU0  ] | EAX=0000ff53  EBX=00010000  ECX=001022e0  EDX=00000000
04641352650i[CPU0  ] | ESP=00102294  EBP=001022b0  ESI=00000000  EDI=00000000
04641352650i[CPU0  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
04641352650i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
04641352650i[CPU0  ] |  CS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
04641352650i[CPU0  ] |  DS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
04641352650i[CPU0  ] |  SS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
04641352650i[CPU0  ] |  ES:0018( 0003| 0|  0) 00000000 ffffffff 1 1
04641352650i[CPU0  ] |  FS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
04641352650i[CPU0  ] |  GS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
04641352650i[CPU0  ] | EIP=001001d8 (001001d8)
04641352650i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
04641352650i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
(0).[4641352650] [0x0000001001d8] 0010:00000000001001d8 (unk. ctxt): mov ds, ax                ; 8ed8
04641352650e[CPU0  ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting

有了这个,当我info gdt再次输入时,它给了我一个非常大的数组,甚至不适合我的终端回滚容量。这是最后几行:

GDT[0xffd8]=??? descriptor hi=0x72670074, lo=0x64696c61
GDT[0xffe0]=16-Bit TSS (available) at 0x6c65725f, length 0xc6275
GDT[0xffe8]=Data segment, base=0x5f726700, limit=0x0002636f, Read-Only, Expand-down, Accessed
GDT[0xfff0]=Data segment, base=0x00657266, limit=0x00086572, Read/Write, Accessed
GDT[0xfff8]=Data segment, base=0x675f6275, limit=0x00057267, Read/Write
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'

它说我想访问我的 GDT 之外的数据。

这是我到目前为止编写的代码:

enum SEG_TYPE {
    // Data
    SEG_TYPE_DRO = 0b0000,
    SEG_TYPE_DRW = 0b0010,
    SEG_TYPE_DROE = 0b0100,
    SEG_TYPE_DRWE = 0b0110,
    // Code
    SEG_TYPE_CEO = 0b1000,
    SEG_TYPE_CER = 0b1010,
    SEG_TYPE_CEOC = 0b1100,
    SEG_TYPE_CERC = 0b1110,
};

enum SEG_AC {
    SEG_AC_KERNEL = 0b11,
    SEG_AC_USER = 0b00,
};

void gdt_entry_init(struct gdt_entry* entry, u32 base, u32 limit, enum SEG_TYPE type, enum SEG_AC access_rights) {
    // Base address
    entry->base_0_15 = base;
    entry->base_16_23 = base >> 16;
    entry->base_24_31 = base >> 24;
    // Limit
    entry->limit_0_15 = limit;
    entry->limit_16_19 = limit >> 16;
    // Segment type
    entry->type = type;
    // Access rights
    entry->dpl = access_rights;
    // AVL
    entry->avl = 0;
    // Default operation set to 32 bits
    entry->db = 1;
    // Code segment
    entry->l = 0;
    // Present (always present)
    entry->p = 1;
    // Descriptor type (code or data)
    entry->s = 1;
    // Granularity (enabled with 4KBytes increment)
    entry->g = 1;
}
struct gdt_entry {
    u32 limit_0_15  : 16;
    u32 base_0_15   : 16;
    u32 base_16_23  : 8;
    u32 type                : 4;
    u32 s                   : 1;
    u32 dpl                 : 2;
    u32 p                   : 1;
    u32 limit_16_19 : 4;
    u32 avl                 : 1;
    u32 l                   : 1;
    u32 db                  : 1;
    u32 g                   : 1;
    u32 base_24_31  : 8;
} __attribute__((packed));

struct gdt_r {
    u16 limit;
    u32 base;
} __attribute__((packed));

struct gdt_entry gdt[6];

void gdt_init() {
    // Null segment
    struct gdt_entry null_entry = { 0 };
    gdt[0] = null_entry;
    // Kernel code segment
    gdt_entry_init(gdt + 1, 0x0, 0xFFFFFFFF, SEG_TYPE_CER, SEG_AC_KERNEL);
    // Kernel data segment
    gdt_entry_init(gdt + 2, 0x0, 0xFFFFFFFF, SEG_TYPE_DRW, SEG_AC_KERNEL);
    // User code segment
    gdt_entry_init(gdt + 3, 0x0, 0x0, SEG_TYPE_CEO, SEG_AC_USER);
    // User data segment
    gdt_entry_init(gdt + 4, 0x0, 0x0, SEG_TYPE_DRO, SEG_AC_USER);
    // TSS
    gdt[5] = null_entry;

    struct gdt_r gdtr;
    gdtr.base = (u32)gdt;
    gdtr.limit = sizeof(gdt);

    asm volatile("lgdt %0\n"
        : /* no output */
        : "m" (gdtr)
        : "memory");

    // 0x10 is the address of the the kernel data segment
    asm volatile("movw 0x10, %%ax\n":);
    asm volatile("movw %%ax, %%ds\n":);
    asm volatile("movw %%ax, %%fs\n":);
    asm volatile("movw %%ax, %%gs\n":);
    asm volatile("movw %%ax, %%ss\n":);
    
    // 0x8 is the address of the kernel code segment
    asm volatile("pushl 0x8\n"
                 "pushl $1f\n"
                 "lret\n"
                 "1:\n"
        : /* no output */);
}

如果你们知道这是怎么回事。

4

1 回答 1

0

事实证明,一个非常聪明的人发现了问题:

  1. 我在写内联汇编时错过了$直接值的前面:
    // 0x10 is the address of the the kernel data segment
    asm volatile("movw $0x10, %%ax\n":);
    asm volatile("movw %%ax, %%ds\n":);
    asm volatile("movw %%ax, %%fs\n":);
    asm volatile("movw %%ax, %%gs\n":);
    asm volatile("movw %%ax, %%ss\n":);
    
    // 0x8 is the address of the kernel code segment
    asm volatile("pushl $0x8\n"
                 "pushl $1f\n"
                 "lret\n"
                 "1:\n"
        : /* no output */);
  1. 我交换了内核和用户的权限,正确的应该是
enum SEG_AC {
    SEG_AC_KERNEL = 0b00,
    SEG_AC_USER = 0b11,
};
于 2021-06-09T12:31:43.637 回答