bootloader - 将处理器切换到保护模式

时间:2011-03-06 15:55:25

标签: assembly operating-system x86 bootloader protected-mode

我很难理解简单的引导装载程序是如何工作的。我正在谈论的引导加载程序是麻省理工学院“操作系统工程”课程中的一个。

首先,让我向您展示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

从外观上看,此代码设置中断表和描述符表,然后打开保护模式。

  1. 为什么我们进入保护模式 在BIOS中?不应该的 bootloader以实模式运行(顺便说一下 - 为什么它需要在真实中运行 模式?)
  2. 我搜索但没有找到任何地方 究竟如何ljmpl指令 工作,是和之间的区别 它和ljmp和常规的jmp - 我 如果有人愿意,我会很感激 指向正确的方向。
  3. 我们为什么要跳跃?什么是 这个指示的目的是什么?
  4. 转到启动加载程序代码 -

    # 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
    
    1. 它说处理器在 实模式 - 但我们只是看到了 BIOS切换到保护模式... 我很困惑 - 这怎么可能 可能的?
    2. 我们如何切换到32位模式?什么 导致处理器神奇地走了 由于ljmp进入32位模式 指令?
    3. 另一件我不明白的事情 - 当我用gdb跟踪bootloader的执行时,我看到正在执行以下指令(这是来自bootloader代码的ljmp指令):

      ljmp   $0x8,$0x7c32
      

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

      ljmp   $0xb866,$0x87c32
      

      完全迷失在这里 - 为什么.asm文件中写入的指令和执行的指令有所不同?我有一个预感,这与保护模式及其如何翻译地址有关,但我真的没有。

      我将不胜感激任何帮助!

2 个答案:

答案 0 :(得分:25)

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

  2. ljmp指定要切换到的地址以及要跳转到的地址。它们非常相似(至少在GAS中)汇编器会将带有2个操作数的jmp切换为ljmp。

  3. ljmp是改变cs寄存器的唯一方法之一。这需要完成以激活保护模式,因为cs寄存器需要包含GDT中代码段的选择器。 (如果你想知道,改变cs的其他方法是远程调用,远程返回和中断返回)

  4. 请参阅第1项。要么BIOS切换回实模式,否则此引导加载程序将无法使用此BIOS。

  5. 参见第3项。它更改cs以指定32位代码段,因此处理器进入32位模式。

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

答案 1 :(得分:2)

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

保护模式只提供比realmode更多的功能:本质上是Intel CPU的保护环特权机制(http://en.wikipedia.org/wiki/Ring_(computer_security),32位模式执行等。

我搜索但没有找到ljmpl指令的确切工作方式,它与ljmp和常规jmp之间的区别 - 如果有人指向正确的方向,我将不胜感激。

ljmpl和ljmp在这里是相同的。

我们为什么要跳跃?这条指令的目的是什么?

这是必需的,如英特尔手册中所述,并在下面的代码中内联记录。

对于实际受保护的转换,它在stage2 bootloader中实现:

http://src.illumos.org/source/xref/illumos-gate/usr/src/grub/grub-0.97/stage2/asm.S#real_to_prot

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

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