当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《在长模式下更改GDT并更新CS》经验,为你挑选了1个好方法。

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

提前致谢。



1> Jester..:

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


我会用`jmpq * fun_ptr; fun_ptr:.quad $ fun; .word $ 8`或32位等效项(如果已知`fun`小于<4GB)。
推荐阅读
php
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有