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



我正在编写一个简单的自制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是正确的方法吗?或者我应该使用其他哪种指令?

提前谢谢。

看起来主要问题是一个简单的拼写错误。在&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

相关内容

  • 没有找到相关文章

最新更新