我正在编写一个简单的自制64位OS,并通过UEFI进行引导。这意味着当我的代码开始执行时,它已经处于long模式,并且启用了分页。
现在,退出UEFI引导服务后,我想用我自己的UEFI构建所有控制结构。
成功更改CR3(分页结构)的内容后,我使用成功加载了新的GDT lgdt
。
问题在于,现在,要正确使用此新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
正确的方法吗?还是我应该使用其他哪条指令?
提前致谢。
看起来主要问题是一个简单的错字。在at&t语法中pushq fun
,其pushq $fun
含义非常不同,前者将内存中的8个字节fun
推入address处,而后者将地址推入fun
(假定它适合立即扩展的32位符号)。
话虽如此,lretq
也应该期望选择器是一个完整的8字节qword,因此它pushw $8
确实应该如此pushq $8
。只要可以读取额外的6个字节,字大小的推入仍将起作用,但是它将使堆栈不平衡。如果您仍然重新加载堆栈指针,则可能没有关系。
避免上述所有陷阱的替代代码如下所示:
sub $16, %rsp movq $8, 8(%rsp) movabsq $fun, %rax mov %rax, (%rsp) lretq