Assembly 引导加载程序-将处理器切换到保护模式

Assembly 引导加载程序-将处理器切换到保护模式,assembly,operating-system,x86,bootloader,protected-mode,Assembly,Operating System,X86,Bootloader,Protected Mode,我很难理解一个简单的引导加载程序是如何工作的。我所说的引导加载程序是MITs课程“操作系统工程”中的一个 首先,让我向您展示BIOS执行的一段汇编代码: [f000:fec3] 0xffec3: lidtw %cs:0x7908 [f000:fec9] 0xffec9: lgdtw %cs:0x7948 [f000:fecf] 0xffecf: mov %cr0,%eax [f000:fed2] 0xffed2: or $0x1,%eax [f000:f

我很难理解一个简单的引导加载程序是如何工作的。我所说的引导加载程序是MITs课程“操作系统工程”中的一个

首先,让我向您展示BIOS执行的一段汇编代码:

[f000:fec3]    0xffec3: lidtw  %cs:0x7908
[f000:fec9]    0xffec9: lgdtw  %cs:0x7948
[f000:fecf]    0xffecf: mov    %cr0,%eax
[f000:fed2]    0xffed2: or     $0x1,%eax
[f000:fed6]    0xffed6: mov    %eax,%cr0
[f000:fed9]    0xffed9: ljmpl  $0x8,$0xffee1
从外观上看,该代码设置中断表和描述符表,然后打开保护模式

  • 为什么我们要进入保护模式 在BIOS中?难道不是吗 引导加载程序以实模式运行(顺便说一句- 为什么它需要在现实中运行 模式?)
  • 我搜索了一下,但什么也没找到 ljmpl指令究竟是如何运行的 工作,这两者之间的区别是什么 它与ljmp和正规jmp-I 如果有人愿意,我将不胜感激 指向正确的方向
  • 我们为什么要跳呢?是什么 本说明的目的是什么 继续讨论引导加载程序代码-

    # Switch from real to protected mode, using a bootstrap GDT
    # and segment translation that makes virtual addresses
    # identical to their physical addresses, so that the
    # effective memory map does not change during the switch.
    lgdt    gdtdesc
    movl    %cr0, %eax
    orl     $CR0_PE_ON, %eax
    movl    %eax, %cr0
    
    # Jump to next instruction, but in 32-bit code segment.
    # Switches processor into 32-bit mode.
    ljmp    $PROT_MODE_CSEG, $protcseg
    
  • 它说处理器在运行 真实模式-但我们刚刚看到 BIOS切换到保护模式。。。 我很困惑——这怎么可能 可能吗
  • 如何切换到32位模式?什么 使处理器神奇地运行 由于ljmp而进入32位模式 指令
  • 还有一件事我不明白——当我用gdb跟踪引导加载程序的执行时,我看到正在执行以下指令(这是引导加载程序代码中的ljmp指令):

    但当我查看.asm文件时,我看到了以下内容:

    ljmp   $0xb866,$0x87c32
    
    这里完全丢失了-为什么写在.asm文件中的指令和执行的指令不同?我有一种预感,这与受保护模式以及它如何转换地址有关,但我真的不明白

    我将感谢任何帮助

  • 一些BIOS实现在进入引导加载程序之前进入保护模式。大多数人没有。BIOS可能会在短时间内切换到保护模式,并在转到引导加载程序之前切换回,这将允许它使用保护模式的一些优点(例如32位是默认地址大小)。引导加载程序应处于实模式的原因是大多数BIOS功能仅在实模式下工作,因此您需要处于实模式才能使用它们

  • 除了要跳转到的地址之外,ljmp还指定要切换到的代码段。它们非常相似(至少在GAS中),汇编器会为您将具有2个操作数的jmp切换为ljmp

  • ljmp是更改cs寄存器的唯一方法之一。需要这样做才能激活保护模式,因为cs寄存器需要包含GDT中代码段的选择器。(如果您想知道,更改cs的其他方法有远调用、远返回和中断返回)

  • 见项目1。BIOS切换回实模式,或者此引导加载程序无法使用此BIOS

  • 见项目3。它将cs更改为指定32位代码段,因此处理器进入32位模式

  • 当您查看.asm文件时,指令被解释为地址大小为32位,但GDB将其解释为地址大小为16位。指令地址处的数据为0xEA 32 7C 08 00 66 B8。EA是跳远操作码。在32位地址空间中,对于0x87C32地址,将使用接下来的四个字节指定地址,但在16位地址空间中,对于0x7C32地址,仅使用两个字节。地址后的2个字节指定请求的代码段,在32位模式下为0xB866,在16位模式下为0x0008。0x66 B8是下一条指令的开始,该指令将一个16位立即数值移动到ax寄存器中,可能是为了设置保护模式的数据段


  • 为什么我们在BIOS中进入保护模式?引导加载程序不应该在实模式下运行吗(顺便说一句,为什么它需要在实模式下运行?)

    受保护模式提供了比realmode更多的功能:本质上是Intel CPU的保护环特权机制(http://en.wikipedia.org/wiki/Ring_(计算机安全)、32位模式执行等

    我搜索了ljmpl指令的具体工作原理,但没有找到它与ljmp和常规jmp之间的区别——如果有人能指出正确的方向,我将不胜感激

    ljmpl和ljmp在这里的上下文是相同的

    为什么要执行跳转?此指令的目的是什么

    这是“英特尔手册”中规定的要求,下面的代码中也有内联规定

    对于从真实到受保护的转换,它在stage2引导加载程序中实现,如下所示:


    正如你所看到的,代码的每个部分都有一个函数,ljmp本质上是清除预取队列,正如《英特尔手册》中所要求的,我不记得在哪里。

    投票结束太宽:一个问题太多。
    ljmp
    使用选择器加载cs,该选择器通常在GDT中选择一个描述符,即DPL=0,32位code段。在执行
    ljmp
    之前,无论是否设置PE,您仍然处于16位代码段中。设置PE会影响加载段寄存器的行为。真正改变模式的是cs选择器加载描述符。
    ljmp   $0xb866,$0x87c32
    
    974   /* load the GDT register */
    975   DATA32  ADDR32  lgdt    gdtdesc
    976 
    977   /* turn on protected mode */
    978   movl    %cr0, %eax
    979   orl $CR0_PE_ON, %eax
    980   movl    %eax, %cr0
    981 
    982   /* jump to relocation, flush prefetch queue, and reload %cs */
    983   DATA32  ljmp    $PROT_MODE_CSEG, $protcseg
    984