X86 使用iret切换到用户模式

X86 使用iret切换到用户模式,x86,nasm,osdev,X86,Nasm,Osdev,我正在编写一个小操作系统,它将以用户模式(特权级别3)执行一些代码。从用户级代码中,我想调用一个中断返回到打印消息的操作系统。现在我真的不关心我的中断处理程序如何接受参数或类似的东西,我真的只希望一个中断处理程序通知我(用户)代码已经执行 我的问题是:如何在用户模式下运行代码?我有一个函数,用于设置带有代码段和数据段(都具有用户模式权限)的本地描述符表。我不明白的是如何将这些段加载到cs、ss和ds。我成功地加载了我的LDT,但我不知道如何实际使用它。我听说我应该使用iret,但我不知道具体怎么

我正在编写一个小操作系统,它将以用户模式(特权级别3)执行一些代码。从用户级代码中,我想调用一个中断返回到打印消息的操作系统。现在我真的不关心我的中断处理程序如何接受参数或类似的东西,我真的只希望一个中断处理程序通知我(用户)代码已经执行

我的问题是:如何在用户模式下运行代码?我有一个函数,用于设置带有代码段和数据段(都具有用户模式权限)的本地描述符表。我不明白的是如何将这些段加载到
cs
ss
ds
。我成功地加载了我的LDT,但我不知道如何实际使用它。我听说我应该使用
iret
,但我不知道具体怎么用

我的另一个问题是我的中断处理程序应该如何工作。假设我为向量号0x40安装了一个中断处理程序,我想打印“hello,user mode!”。我知道如何设置一个中断处理程序,但我并不完全理解当从用户模式进入内核中断处理程序时,如何切换上下文。我知道
cs
寄存器必须更改,因为我的例程将从IDT条目中指定的代码段运行。我也知道堆栈选择器可能也会改变,但我不能确定这一点


有人能给我解释一下,当一个中断门被调用时,上下文会发生什么变化吗

可以使用
iret
进入第3环,因为它的工作方式已经被记录在案。当您收到中断时,处理器将按下:

  • 堆栈段和指针(ss:esp),作为4个字
  • EFLAGS
  • 返回代码段和指令指针(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
    
    如果需要完整的工作示例,请参阅



    发出中断时,处理器读取IDT以获取ISR的正确代码段和指令指针。然后,它查看TSS以找到新的堆栈段和指针。它适当地更改
    ss
    esp
    ,然后将旧值推送到新堆栈中。它不会更改任何数据段寄存器。如果需要访问ISR中的内存,则必须手动执行此操作。

    您也可以执行retf。远返回到权限较低的代码段将导致新的ss和sp从权限堆栈中弹出

    只要确保对远呼叫执行远返回,对中断执行IRET。它们之间的唯一区别是堆栈上存在标志,但明智的做法是不要混淆它们


    另外,不要忘记异常有时会在堆栈上推送错误代码。

    好的,你回答了我的问题,还有更多!我现在的理解是,我需要一个包含我的内核堆栈的TSS,等等。谢谢你的帮助。我登录只是为了升级这个!谢谢对于其他人,我也推荐osdev和英特尔ISA手册。他们真的把事情弄清楚了。你说iNet不改变数据段是什么意思?你说的是通用寄存器中的值吗?@DanHoynoski不,我指的是数据段寄存器,
    ds
    es
    ,等等。它们没有改变,所以任何使用的都必须手动设置。