使用 iret 切换到用户模式
Posted
技术标签:
【中文标题】使用 iret 切换到用户模式【英文标题】:Switching to User-mode using iret 【发布时间】:2011-10-17 01:42:35 【问题描述】:我正在编写一个小型操作系统,它将在用户模式(权限级别 3)下执行一些代码。从该用户级代码中,我想将中断调用回打印消息的操作系统。现在我并不关心我的中断处理程序如何接受参数或类似的东西,我真的只是想要一个中断处理程序通知我(用户)代码已经执行。
我的问题是:如何在用户模式下运行代码?我有一个函数可以设置一个带有代码段和数据段的本地描述符表(都具有用户模式权限)。我不明白我应该如何将这些段加载到cs
、ss
和ds
。我成功加载了我的 LDT,但我不知道如何实际使用它。我听说我应该使用iret
,但我不明白具体怎么做。
我的另一个问题是我的中断处理程序应该如何工作。假设我为向量编号 0x40 安装了一个中断处理程序,我想打印“你好,用户模式!”。我知道如何设置中断处理程序,但我不完全了解从用户模式进入内核中断处理程序时如何切换上下文。我知道cs
寄存器必须更改,因为我的例程将从 IDT 条目中指定的代码段运行。我也知道堆栈选择器也可能会发生变化,但我不能确定这一点。
有人可以向我解释一下调用中断门时会发生哪些上下文变化吗?
【问题讨论】:
【参考方案1】:可以使用iret
来拨打响铃 3,因为它的工作方式已记录在案。当您收到中断时,处理器会推送:
-
堆栈段和指针 (ss:esp),为 4 个字
标志
返回代码段和指令指针 (cs:eip),为 4 个字
错误代码(如果需要)。
iret
通过撤消步骤 1-3 工作(如果需要,ISR 负责撤消步骤 4)。我们可以利用这个事实通过将所需信息压入堆栈并发出iret
指令来到达环 3。确保您的代码和堆栈段中有正确的 CPL(低两位应在每个中设置)。但是,iret
不会更改任何数据段,因此您需要手动更改它们。您使用mov
指令来执行此操作,但您将无法在执行此操作和切换环之间读取堆栈外的数据。
cli
mov ax, Ring3_DS
mov ds, eax
push dword Ring3_SS
push dword Ring3_ESP
pushfd
or dword [esp], 0x200 // Set IF in EFLAGS so that interrupts will be reenabled in user mode
push dword Ring3_CS
push dword Ring3_EIP
iret
如果您想要一个完整的工作示例,请参阅this tutorial。
发出中断时,处理器会读取您的 IDT 以获取 ISR 的正确代码段和指令指针。然后它会查看您的 TSS 以找到新的堆栈段和指针。它会适当地更改ss
和esp
,然后将旧值推送到新堆栈。它确实不更改任何数据段寄存器。如果您需要访问 ISR 中的内存,则必须手动执行此操作。
【讨论】:
好的,你回答了我的问题,还有更多!我现在的理解是,我需要一个包含我的内核堆栈等的 TSS。谢谢你的帮助。 我登录只是为了点赞!谢谢!同样对于其他任何人,我推荐 osdev,以及英特尔 ISA 手册。他们真的把事情弄清楚了。 你是什么意思IRET不改变数据段?您是在谈论通用寄存器中的值吗? @DanHoynoski 不,我指的是数据段寄存器,ds
、es
等。它们没有改变,所以必须手动设置。【参考方案2】:
你也可以做一个 retf.远返回到特权较低的代码段将导致新的 ss 和 sp 从特权堆栈中弹出。
只要确保你为远调用做远返回,为中断做 irets。它们之间的唯一区别是堆栈上存在标志,但不要混淆它们是明智的。
另外,不要忘记异常有时会将错误代码推入堆栈。
【讨论】:
以上是关于使用 iret 切换到用户模式的主要内容,如果未能解决你的问题,请参考以下文章