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

Posted

技术标签:

【中文标题】引导加载程序 - 将处理器切换到保护模式【英文标题】:bootloader - switching processor to protected mode 【发布时间】:2011-07-09 20:27:47 【问题描述】:

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

首先,让我给你看一段 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 指令到底如何 工作,是之间的区别 it 和 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 指令):

ljmp   $0x8,$0x7c32

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

ljmp   $0xb866,$0x87c32

完全迷路了——为什么写在.asm文件中的指令和执行的指令不一样?我有一种预感,这与保护模式以及它如何转换地址有关,但我真的不明白。

我将不胜感激!

【问题讨论】:

投票结束过于广泛:一个问题太多。 【参考方案1】:

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

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

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

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

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

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

对于real-to-protected的转换,这里是在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本质上是清除预取队列,按照英特尔手册的要求,我不记得在哪里了。

【讨论】:

ljmp 使用选择器加载 cs,该选择器通常选择 GDT 中的描述符,即 DPL=0,32 位代码段。在ljmp 执行之前,无论是否设置了PE,您仍然处于16 位代码段中。设置的 PE 会影响加载段寄存器的行为。真正改变模式的是加载描述符的cs选择器。【参考方案2】:

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

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

    ljmp 是更改 cs 寄存器的唯一方法之一。这需要完成以激活保护模式,因为 cs 寄存器需要包含 GDT 中代码段的选择器。 (如果你想知道,改变cs的其他方法是far call、far return和interrupt return)

    参见第 1 项。要么 BIOS 切换回实模式,要么此引导加载程序无法与此 BIOS 一起使用。

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

    当您查看 .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 寄存器中,可能是为了将数据段设置为保护模式。

【讨论】:

以上是关于引导加载程序 - 将处理器切换到保护模式的主要内容,如果未能解决你的问题,请参考以下文章

01 linux011 引导和保护模式的初始设置

切换到保护模式后“呼叫”

在loader程序中涉及到的CPU模式切换

Lab_1:练习3——分析bootloader进入保护模式的过程

第十一课 实模式到保护模式 中

GRUB 是不是切换到保护模式?