【发布时间】:2016-03-19 19:10:43
【问题描述】:
我正在编写一个简单的自制 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 是这样做的正确方法吗?或者我应该使用哪个其他指令?
提前致谢。
【问题讨论】:
-
信息不足。见minimal reproducible example。你怎么知道你声称的事情是有效的?旁注,您应该使用
pushq $8,因为retfq需要一个qword。另外我希望你知道rsp仍然是指当前(缓存的)堆栈段,所以push和retf将使用它。最后,使用未对齐的rsp通常是不受欢迎的,即使 cpu 会接受它。 -
@Jester 我确信某些指令有效,因为我独立测试了它们并且代码运行良好。另外,旧的堆栈段也可以,因为它覆盖了所有的地址空间(实际上,在长模式下,所有段都覆盖了整个地址空间,并且仅用于访问权限;段限制总是被忽略;段基仅有效
fs和gs)。 -
是的,但是分页可能会将地址
100映射到一些无法访问的内存中。 -
@Jester 你是对的。我应该指定我多次检查了我的分页结构,它们看起来没问题。
-
嗯,你的意思是
pushq $fun可能吗?另请注意,立即推送仅对扩展的 32 位进行符号化。我使用的代码更像:sub $16, %rsp; movq $8, 8(%rsp); movabsq $fun, %rax; mov %rax, (%rsp); lretq.
标签: assembly 64-bit x86-64 osdev gdt