Protected Mode

来自osdev
(重定向自Protected mode
跳到导航 跳到搜索

“实模式”是一种简单的16位模式,存在于所有x86处理器上。 实模式是第一个x86模式设计,在保护模式诞生之前,许多早期操作系统都使用实模式。 出于兼容性目的,所有x86处理器 begin execution都处于实模式。

信息

所有现代操作系统(Windows、Linux等)在受保护模式下运行,这是因为实际模式存在许多限制和问题 显示(请参见下文和Real Mode OS Warning页面)。旧的操作系统(如DOS)和程序以实模式运行,因为它是当时唯一可用的模式。有关如何从实模式切换到Protected Mode的信息,请参阅相应的文章。

注意:有一种称为Virtual 8086 mode的模式,允许在受保护模式下运行的操作系统模拟单个应用程序的实模式分段模型。 这可用于允许受保护模式操作系统在需要时仍可访问BIOS等功能。

在下面,你会发现一个利弊清单。 这些主要是“与保护模式相比”。

缺点

  • 可用内存少于1 MB。
  • 没有基于硬件的内存保护(GDT),也没有虚拟内存。
  • 没有内置的安全机制来防止错误或恶意应用程序。
  • 默认CPU操作数长度仅为16位。
  • 提供的内存寻址模式比其他CPU模式更具限制性。
  • 访问超过64k的数据需要使用难以使用的段寄存器。

优点

  • BIOS安装设备驱动程序来控制设备和处理中断。
  • BIOS函数为操作系统提供高级的低级API函数集合。
  • 由于缺少要检查的描述符表和更小的寄存器,内存访问速度更快。

常识错误

程序员通常认为,由于实模式默认为16位,因此32位寄存器不是 可到达的 事实并非如此。

所有32位寄存器(EAX,…)仍然可用,只需添加“操作数大小覆盖” 在任何指令的开头加上前缀“(0x66)。 如果需要,您的汇编程序可能会为您执行此操作 您只需尝试使用32位寄存器。

存储器寻址

在实模式下,有略多于1MB的“可寻址”内存(包括高内存区)。请参见检测内存(x86)和[[内存映射(x86)],以确定实际“可用”的数量。可用容量将远小于1MB。 通过段:偏移系统使用Segmentation进行内存访问。

有六个16位段寄存器:CS、DS、ES、FS、GS和SS。 使用段寄存器时,地址用以下符号表示(其中“段”是段寄存器中的值,“偏移量”是地址寄存器中的值):

 12F3  :  4B27
   ^       ^
Segment   Offset

段和偏移量通过以下等式与物理地址相关: 物理地址=段*16+偏移量

因此,12F3:4B27对应于物理地址0x17A57。任何物理地址都可以用不同的段和偏移量以多种方式表示。例如,物理地址0x210可以是0020:0010、0000:0210或0021:0000。

堆栈

SS和SP是16位段:偏移寄存器,指定20位物理地址(如上所述),这是堆栈的当前“顶部”。 堆栈存储16位字,向下增长,并且必须在字(16位)边界上对齐。 每次程序执行PUSH、POP、CALL、INT或RET操作码时,以及BIOS处理任何硬件中断时,都会使用它。

高存储区

如果将DS(或任何段寄存器)的值设置为0xFFFF,它将指向低于1MB的16字节的地址。 如果然后使用该段寄存器作为基址,偏移量为0x10到0xFFFF,则可以访问0x100000到0x10FFFF之间的物理内存地址。 这个超过1MB的区域(几乎64KB)在实模式中称为“高内存区域”。 请注意,您必须激活A20地址行才能工作。

寻址方式

实模式默认使用16位寻址模式。汇编程序员通常熟悉 更常见的32位寻址模式,可能需要进行调整——因为可用的寄存器 在16位寻址模式下,用作“指针”的功能要有限得多。运行的典型程序 在实数模式中,通常限制可用的字节数,并且每个字节需要额外的一个字节 改为使用32位寻址的操作码。

注意,您仍然可以在实模式中使用32位寻址模式,只需添加“地址大小” 将前缀“(0x67)覆盖到任何指令的开头。 如果需要,您的汇编程序可能会为您执行此操作 您只需尝试使用32位寻址模式。 但您仍然受到当前的“限制”的限制 在每次内存访问中使用的段(在实模式下总是64K--非实模式可以更大)。

  • [BX + val]
  • [SI + val]
  • [DI + val]
  • [BP + val]
  • [BX + SI + val]
  • [BX + DI + val]
  • [BP + SI + val]
  • [BP + DI + val]
  • [address]

保护模式切换到实模式

如上所述,受保护模式操作系统可以使用Virtual 8086 mode模式访问BIOS功能。 然而,VM86模式有其自身的复杂性和困难。 一些操作系统设计者认为,在需要访问BIOS功能的情况下,暂时返回到实模式更简单、更干净。 这需要创建一个特殊的环0程序,并将其放置在一个可以在实模式下访问的物理内存地址中。

The OS usually needs to pass an information packet about which BIOS function to execute.

该计划需要经过以下步骤:

  1. 禁用中断:
    • 使用CLI关闭可屏蔽中断。
    • 禁用NMI(可选)。
  2. 关闭分页
    • 将控制转移到1:1页面。
    • 确保GDTIDT处于1:1的页面中。
    • 清除第零个控制寄存器中的PG标志。
    • 将第三个控制寄存器设置为0。
  3. 将GDT与16位表一起使用(如果已经有一个表可用,则跳过此步骤):
    • 创建具有16位数据和代码段的新GDT:
      • 限制:0xFFFFF
      • 基数:0x0
      • 16位
      • 权限级别:0
      • 粒度:0
      • 读写:1
    • 加载新GDT,确保当前使用的选择器保持不变(cs/ds/ss中的索引将是新GDT中原始段的副本)
  4. 远跳到16位保护模式:
    • Far jump to 16-bit protected mode with a 16-bit segment index.
  5. Load data segment selectors with 16-bit indexes:
    • Load ds, es, fs, gs, ss with a 16-bit data segment.
  6. Load real mode IDT:
    • Limit: 0x3FF
    • Base 0x0
    • Use lidt
  7. Disable protected mode:
    • Set PE bit in CR0 to false.
  8. Far jump to real mode:
    • Far jump to real mode with real mode segment selector (usually 0).
  9. Reload data segment registers with real mode values:
    • Load ds, es, fs, gs, ss with appropriate real mode values (usually 0).
  10. Set stack pointer to appropriate value:
    • Set sp to stack value that will not interfere with real mode program.
  11. Enable interrupts:
    • Enable maskable interrupts with STI.
  12. Continue on in real mode with all bios interrupts.

x86 汇编示例

[bits 16]

idt_real:
dw 0x3ff		; 256 entries, 4b each = 1K
dd 0			; Real Mode IVT @ 0x0000

savcr0:
dd 0			; Storage location for pmode CR0.

Entry16:
; We are already in 16-bit mode here!

	cli			; Disable interrupts.

	; Need 16-bit Protected Mode GDT entries!
	mov eax, DATASEL16	; 16-bit Protected Mode data selector.
	mov ds, eax
	mov es, eax
	mov fs, eax
	mov gs, eax
	mov ss, eax


	; Disable paging (we need everything to be 1:1 mapped).
	mov eax, cr0
	mov [savcr0], eax	; save pmode CR0
	and eax, 0x7FFFFFFe	; Disable paging bit & disable 16-bit pmode.
	mov cr0, eax

	jmp 0:GoRMode		; Perform Far jump to set CS.

GoRMode:
mov sp, 0x8000		; pick a stack pointer.
mov ax, 0		; Reset segment registers to 0.
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
lidt [idt_real]
sti			; Restore interrupts -- be careful, unhandled int's will kill it.

另见

文章

外部链接

 ** Volume 3A: System Programming Guide, Part 1,Chapter 20:8086 EMULATION,which is a detailed reference about real mode using 32-bit addressing mode

de:Real Mode