我正在编写一个小型操作系统,它将在用户模式下执行一些代码(权限级别3)。从该用户级代码,我想将中断调回到打印消息的操作系统。现在我真的不关心我的中断处理程序如何接受参数或类似的东西,我真的只想要一个中断处理程序来通知我(用户)代码已执行。
我的问题是:如何在用户模式下运行代码?我有一个函数,它设置一个带有代码段和数据段的本地描述符表(都具有用户模式权限)。我不明白的是我应该如何将这些细分加载到cs
,ss
和ds
。我成功加载了我的LDT,但我不知道如何实际使用它。我听说我应该使用iret
,但我不确切地知道如何。
我的另一个问题是我的中断处理程序应该如何工作。假设我为向量号0x40安装了一个中断处理程序,我想打印“你好,用户模式!”。我知道如何设置中断处理程序,但我不完全理解从用户模式进入内核中断处理程序时如何切换上下文。我知道cs
寄存器必须更改,因为我的例程将从我的IDT条目中指定的代码段运行。我也明白堆栈选择器可能也会改变,但我无法确定这一点。
有人可以向我解释当调用中断门时会发生什么情况变化吗?
答案 0 :(得分:23)
使用iret
可以完成第3次响铃,因为它的工作方式已经记录在案。当您收到中断时,处理器会按下:
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中的内存,则必须手动执行此操作。
答案 1 :(得分:1)
你也可以做一个retf。远程返回较少特权的代码段将导致新的ss和sp从特权栈中弹出。
请确保为远程呼叫和中断的远程返回做远程返回。它们之间的唯一区别是堆栈上存在标志,但明智的是不要混淆它们。
另外,不要忘记异常有时会在堆栈上推送错误代码。