简体   繁体   English

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

[英]Change GDT and update CS while in long mode

I'm writing a simple home-made 64-bit OS, booting it via UEFI. 我正在编写一个简单的自制64位OS,并通过UEFI进行引导。 This means that when my code starts executing, it is already in long mode, with paging enabled. 这意味着当我的代码开始执行时,它已经处于long模式,并且启用了分页。

Now, after exiting the UEFI boot services, I want to replace all control structures built by UEFI with my own. 现在,退出UEFI引导服务后,我想用我自己的UEFI构建所有控制结构。

After successfully changing the contents of CR3 (paging structures), I successfully loaded a new GDT using lgdt . 成功更改CR3(分页结构)的内容后,我使用lgdt成功加载了新的GDT。

The problem is that now, to correctly use this new GDT, I need to move a new value into CS. 问题在于,现在,要正确使用此新GDT,我需要将新值移至CS中。 Online I found lots of tutorials on how to do that while switching from 32-bit to 64-bit, but almost nothing about long mode to long mode. 在网上,我找到了许多有关如何从32位切换到64位的方法的教程,但是从长模式到长模式几乎一无所获。

I think I should use a far jump, but I didn't manage to do that with this code (AT&T syntax): 我想我应该跳个很远的距离,但是我没有用以下代码(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)

Not having any IDT in place, this code triple faults at retfq . 没有适当的IDT,此代码使retfq发生三重错误。

EDIT: I checked my paging structures, and I'm quite sure they are not the cause of the problems. 编辑:我检查了我的分页结构,并且我很确定它们不是问题的原因。 In fact, the code runs fine without the last three instructions. 实际上,在没有最后三个指令的情况下,代码可以正常运行。 The problem is that I need a way to update the CS, that in my code still refers to the old segment built by UEFI. 问题是我需要一种更新CS的方法,在我的代码中仍然引用UEFI构建的旧段。 Is retfq the correct way of doing this? retfq是这样做的正确方法吗? Or which other instruction should I use? 还是我应该使用其他哪条指令?

Thanks in advance. 提前致谢。

Looks like the main issue was a simple typo. 看起来主要问题是一个简单的错字。 In at&t syntax pushq fun and pushq $fun mean very different things, the former pushes the 8 bytes in memory at address fun while the latter pushes the address of fun (assuming it fits into a 32 bit sign extended immediate). 在at&t语法中, pushq funpushq $fun含义非常不同,前者在地址fun处将内存中的8个字节压入,而后者将fun的地址中压(假定它适合立即扩展的32位符号)。

That said, lretq also expects the selector as a full 8-byte qword so pushw $8 should really pushq $8 . 也就是说, lretq还希望选择器是一个完整的8字节qword,因此pushw $8应该确实是pushq $8 The word-sized push will still work as long as the extra 6 bytes are readable, but it will unbalance the stack. 只要可以读取额外的6个字节,字大小的推入仍将起作用,但是它将使堆栈不平衡。 This might not matter if you reload the stack pointer anyway. 如果您仍然重新加载堆栈指针,则可能没有关系。

An alternative code that avoids all of the above pitfalls could look like: 避免上述所有陷阱的替代代码如下所示:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM