【问题标题】:GDT Segmented MemoryGDT 分段内存
【发布时间】:2013-01-17 16:29:39
【问题描述】:

我正在尝试构建自己的内核,现在正在设置 GDT。我为加载程序使用了一个程序集文件,并调用了用 C 编写的内核,并试图让 GDT 工作。内核从 GRUB 引导并通过 GRUB 刷新 GDT 设置。当我将 GDT 条目设置为段(具有适当的限制和偏移量)时,我假设在 QEMU 以及当我从 pendrive 启动时,内核重新启动时都会出现三重故障。

我的问题是:

我可以为 x86 架构实现分段模型吗?是否需要在保护模式下完成?作业完成后如何退出保护模式?

我会在这里发布代码,但其中大部分来自教程,如果我将程序集和 C 代码混在一起,它会变得一团糟。主要的是,如果我在 C 内核中将代码段和数据段条目的基础都设置为“0”之外的任何其他操作,内核就会重新启动。此外,当我将粒度设置为禁用 4KB 分页时,也会发生同样的事情。如果需要,请询问更多详细信息。谢谢:) :)

编辑:这是我用来链接 asm 引导加载程序和 C 内核文件的 linker.ld 文件。我已经发布了与内存分段相关的 asm 和 C 文件中的段:

链接器:

ENTRY (loader)

SECTIONS
{
    . = 0x00100000;

    .text ALIGN (0x1000) :
    {
        *(.text)
    }

    .rodata ALIGN (0x1000) :
    {
        *(.rodata*)
    }

    .data ALIGN (0x1000) :
    {
        *(.data)
    }

    .bss :
    {
        sbss = .;
        *(COMMON)
        *(.bss)
        ebss = .;
    }
}

用于设置 GDT 和实例化函数的 C 函数:

void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
    /* Setup the descriptor base address */
    gdt[num].base_low = (base & 0xFFFF);
    gdt[num].base_middle = (base >> 16) & 0xFF;
    gdt[num].base_high = (base >> 24) & 0xFF;

    /* Setup the descriptor limits */
    gdt[num].limit_low = (limit & 0xFFFF);
    gdt[num].granularity = ((limit >> 16) & 0x0F);

    /* Finally, set up the granularity and access flags */
    gdt[num].granularity |= (gran & 0xF0);
    gdt[num].access = access;
}

void gdt_install()
{
    /* Setup the GDT pointer and limit */
    gp.limit = (sizeof(struct gdt_entry) * 35) - 1;
    gp.base = &gdt;

    /* Our NULL descriptor */
    gdt_set_gate(0, 0, 0, 0, 0);

   gdt_set_gate(1, 0x00000000, 0xFFFFFFFF, 0x9A, 0xCF); //Setting Code Segment


  gdt_set_gate(2, 0x00000000, 0xFFFFFFFF, 0x92, 0xCF); //Setting Data Segment

  //In the above two, if the second parameter is anything other 
  //than 0 i.e. base is not 0, the kernel doesn't run. 
 //Moreover, setting the last to 0x4F, which is byte accessing rather 
 //than 4KB paging gives the same malfunction too. 

 //gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF);
  //gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF);     
    //TASK STATE SEGMENT -1 
    //TASK STATE SEGMENT -2 

    //gdt_set_gate(3, 0, 0xFFFFFFFF, 0x89, 0xCF);
   // gdt_set_gate(4, 0, 0xFFFFFFFF, 0x89, 0xCF);   









    /* Flush out the old GDT and install the new changes! */
    gdt_flush();
}

最后,用 ASM 编写的 GDT 刷新函数:

gdt_flush:    
    lgdt [gp]        ; Load the GDT with our '_gp' which is a special pointer   
    ;ltr [0x18]
    mov ax, 0x10      ; 0x10 is the offset in the GDT to our data segment
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp 0x08:flush2   ; 0x08 is the offset to our code segment: Far jump!
flush2:
    ret

我知道 C 函数和 ASM 是正确的,因为它们使用平面内存模型。我需要对此进行任何具体更改吗?我想就链接器文件提供一些建议以设置分段或分段分页。

【问题讨论】:

  • 您不应该使用分段。您应该使用分页。分段是古老的,不会在较新的英特尔处理器上得到支持。只需使用分页的标准。
  • @Linuxios 我不太确定。即使在 64 位模式下,您仍然使用 GS 来组织对线程本地存储 (TLS) 的访问。
  • 我们来看一个例子。假设gdt_install() 位于偏移量 0x123456,并且在您以 CS.base = 0 开始时可运行。在这种情况下,CPU 从物理地址 0x123456(基址 + 偏移量)获取 gdt_install() 的第一条指令。然后将 CS.base 更改为 0x10000。在这种情况下,CPU 仍然需要从物理地址 0x123456 获取第一条指令(因为那是代码在内存中的位置,所以您没有移动它!),而是从物理地址 0x133456 获取它。你能看出问题吗?
  • 是的,我看到了问题所在。我需要让加载程序将整个函数或至少在第一次调用 gdt_install() 之后的部分移动到代码段偏移量。我的问题是,链接器在这一切中扮演什么角色?
  • @alexey:是的。但是您仍然应该对其他所有内容使用分页。

标签: x86 kernel gdt


【解决方案1】:

我可以为 x86 架构实现分段模型吗?

当然可以。

是否需要在保护模式下完成?

您只有两个选择:尴尬的实模式和 64K 和其他限制以及保护模式。

作业完成后如何退出保护模式?

简而言之,您关闭页面转换(如果它打开),跳转到 16 位代码段,使用指向与实模式兼容的描述符的选择器加载段寄存器,清除 CR0.PE。还有更多细节(例如任务切换、中断处理)。您可以在官方文档和一些在线教程中找到它们。

如果我在 C 内核中将代码段和数据段条目的基数都设置为“0”之外的任何其他操作,内核就会重新启动。

您必须了解 x86 代码不是位置独立的,如果将其重新定位到地址 X 将无法正常运行,而是加载到地址 Y。这意味着不仅应调整段描述符的基数以移动您的代码/数据,但链接器也应该将可执行映像重新定位到另一个位置。一个简单的错误就足以让这不起作用。

此外,当我将粒度设置为禁用 4KB 分页时,也会发生同样的情况。

这种说法没有任何意义。您不会通过使用描述符的粒度位来禁用分页。

如果需要,请询问更多详细信息。

为我们提供足够的信息来帮助您解决问题是您的职责。正如目前所说的那样,这个问题缺乏细节,无法完全回答。你有一些错误的代码,但你没有向我们展示,我们能提供什么帮助?

【讨论】:

  • 我已经添加了相应的代码。如果您能再看一遍并告诉我,我将不胜感激。谢谢:) :)
【解决方案2】:

加载 GDT 后在你的 asm 代码中使用它。

mov eax, cr0
or eax, 1b
mov cr0, eax

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-04
    • 2012-07-17
    • 1970-01-01
    • 2013-08-18
    • 2019-10-06
    • 2012-12-26
    • 1970-01-01
    • 2016-11-29
    相关资源
    最近更新 更多