在长模式下更改GDT并更新CS

时间:2015-12-14 10:31:04

标签: assembly 64-bit x86-64 osdev gdt

我正在编写一个简单的自制64位操作系统,通过UEFI启动它。这意味着当我的代码开始执行时,它已经处于长模式,启用了分页。

现在,退出UEFI引导服务后,我想用自己的UEFI替换所有构建的控制结构。

成功更改CR3(分页结构)的内容后,我使用lgdt成功加载了新的GDT。

现在的问题是,为了正确使用这个新的GDT,我需要将一个新值移到CS中。在线我从32位切换到64位时发现了很多关于如何做到这一点的教程,但从长模式到长模式几乎没有。

我认为我应该使用远程跳转,但我没有设法使用此代码(AT& T语法):

mov %rax, %cr3   # load paging structures (it works)
lgdt 6(%rcx)     # load gdt (it works)
mov $100, %rsp   # update stack pointer (it works)

# now what I tried unsuccessfully:
pushw $8         # new code segment selector
pushq fun        # function to execute next
retfq            # far return (pops address and code segment)

没有任何IDT,此代码在retfq处出现三重错误。

编辑:我检查了我的分页结构,我很确定它们不是问题的原因。事实上,代码运行正常,没有最后三条指令。问题是我需要一种更新CS的方法,在我的代码中仍然引用UEFI构建的旧段。 retfq这是正确的做法吗?或者我应该使用哪种其他指令?

提前致谢。

1 个答案:

答案 0 :(得分:5)

看起来主要问题是一个简单的拼写错误。在at& t语法pushq funpushq $fun意味着非常不同的事情,前者在地址fun的内存中推送8个字节,而后者推送fun的地址(假设它适合立即扩展的32位符号。

也就是说,lretq也期望选择器为完整的8字节qword,因此pushw $8应该pushq $8。只要额外的6个字节是可读的,字大小的推送仍然可以工作,但它会使堆栈失衡。如果你重新加载堆栈指针,这可能无关紧要。

避免上述所有陷阱的替代代码可能如下所示:

sub $16, %rsp
movq $8, 8(%rsp)
movabsq $fun, %rax
mov %rax, (%rsp)
lretq