我正在尝试构建自己的内核,现在正在设置GDT。我为加载器使用了一个汇编文件,并调用了用C编写的内核,试图让GDT工作。内核从GRUB引导,并通过GRUB刷新GDT设置。当我将我的GDT条目设置为段(具有适当的限制和偏移量)时,我假设在内核重新引导时会出现三重故障,无论是在QEMU中还是在引导挂起驱动器时都是如此。
我的问题是:
我可以为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函数是正确的,因为它们使用平面内存模型。是否有任何具体的变化,我需要做的,我想要一些关于链接器文件,以设置分段或分段分页的一些建议。
发布于 2013-02-03 04:31:01
我可以为x86架构实现分段模型吗?
当然,你可以。
是否需要在保护模式下执行此操作?
你只有两个选择:真实模式及其笨拙和64K和其他限制和保护模式。
作业完成后,如何退出保护模式?
简而言之,你关闭页面转换(如果开启),跳转到16位代码段,用选择器加载段寄存器,指向与实模式兼容的描述符,清除CR0.PE。还有更多细节(例如任务切换,中断处理)。您可以在官方文档和一些在线教程中找到它们。
如果我在C内核中将代码段和数据段条目的基数都设置为'0‘以外的任何操作,内核就会重新启动。
您必须了解,x86代码不是位置独立的,如果它被重新定位到地址X,它将不会正常工作,而是加载到地址Y。这意味着不仅应该调整段描述符的基数,以便将代码/数据移动到某个位置,而且链接器也应该将可执行映像重新定位到另一个位置。一个简单的错误就足以让它不起作用。
此外,当我将粒度设置为禁用4KB分页时,也会发生同样的情况。
这句话没有任何意义。您不能通过操作描述符的粒度位来禁用分页。
如果需要,请询问更多详细信息。
你的工作是为我们提供足够的信息来帮助你。这个问题,就像目前所说的那样,缺乏细节来完全回答。你有一些错误代码,但是你没有向我们展示它,我们怎么能帮助你呢?
发布于 2016-12-04 18:35:36
在加载GDT之后,在asm代码中使用它。
mov eax, cr0
or eax, 1b
mov cr0, eaxhttps://stackoverflow.com/questions/14661916
复制相似问题