3

我最近迷上了内核开发,并从 OSDev Wiki 上的基本教程开始。实现 Hello World 示例后,我继续尝试创建全局描述符表。我从网上的各种来源拼凑了一些 GDT 代码,但最终失败了。我的实现是否有问题,如果不是立即清楚,是否有任何来源可以提供更多信息?

简而言之,以下具有 GDT 的内核实现无法使用 GRUB 加载。我正在使用gccand进行编译as,可以提供所需的任何其他信息。

靴子

.section .text
.global _start
.type _start, @function
_start:
    movl $stack_top, %esp
    call kernel_main
    cli
    hlt
.Lhang:
    jmp .Lhang
.size _start, . - _start

.global gdt_flush

gdt_flush:
    cli
    movl    -4(%esp), %eax
    lgdt    (%eax)
    movw    $0x10, %ax
    movw    %ax, %ds
    movw    %ax, %es
    movw    %ax, %fs
    movw    %ax, %gs
    movw    %ax, %ss            //the inclusion of this line or the following
    jmp $0x08, $.flush      //prevents the kernel from loading
.flush: 
    ret

.section .bootstrap_stack
stack_bottom:
.skip 16384
stack_top:

内核.c

void kernel_main() {
    gdt_install();
    ...
}

gdt.c

struct gdt_entry {
  uint16_t limit_low;
  uint16_t base_low;
  uint8_t base_middle;
  uint8_t access;
  uint8_t granularity;
  uint8_t base_high;
}__attribute__((packed));

struct gdt_ptr {
  uint16_t limit;
  uint32_t base;
}__attribute__((packed));

struct gdt_entry gdt[3];
struct gdt_ptr gp;

extern void gdt_flush(struct gdt_ptr *);

void gdt_set_gate(uint32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) {
  gdt[num].base_low = (base & 0xFFFF);
  gdt[num].base_middle = (base >> 16) & 0xFF;
  gdt[num].base_high = (base >> 24) & 0xFF;

  gdt[num].limit_low = (limit & 0xFFFF);
  gdt[num].granularity = (limit >> 16) & 0x0F;

  gdt[num].granularity |= (gran & 0x0F);
  gdt[num].access = access;
}

void gdt_install() {
  gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
  gp.base = (uint32_t) &gdt;

  gdt_set_gate(0, 0, 0, 0, 0);
  gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
  gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);

  gdt_flush(&gp);
}
4

1 回答 1

2

似乎有几个问题。我没有检查您的 GDT 条目的特定位(这是一个必须自己使用英特尔手册完成的工作)。

第一件事(现在不会导致问题,但将来可能会这样做)是您指定并使用 4 字节宽的限制,尽管 GDT 仅适用于 20 位。您应该更改您的gdt_install函数以仅通过 20 位限制并将其记录在注释中以供将来使用。另一种解决方案当然是将参数向右移动 12 位,但它的意义不大,下次您回到 GDT 管理时可能会做出不同的解释。

第二件事似乎不正确是您获取参数的方式gdt_flush。堆栈向下增长,因此最后推送的项目位于(%esp)(即指令推送的返回地址call)并且您想要的参数位于4(%esp)

我假设您已经处于保护模式并且实际的 GDT 已经由引导加载程序设置,所以我看不出有任何明显的原因导致另一个远跳(至少消耗三个时钟),尽管代码并不总是如此段直接放在空段之后。我不喜欢那个跳转是用作跳转目的地的标签。我建议检查它,因为它是一个需要绝对值的跳远。

于 2013-08-15T09:03:41.583 回答