Linux x86: 在受保护的内核模式下,实模式地址 space 映射到哪里?

Linux x86: Where is the real mode address space mapped to in protected kernel mode?

在 x86 平台上的 Linux 运行 中,实模式地址 space 在受保护的内核模式下映射到哪里?在内核模式下,线程可以直接访问内核地址space。内核在低8MB,页面table在某个位置等(如描述here). But where does the real mode address space go? Can it be accessed directly? For example the BIOS and BIOS addons (See here)?

(我的x86-fu有点弱。我会添加一些标签,以便其他人可以(希望)纠正我,如果我有任何谎言。)

物理地址在实模式和保护模式下是一样的。唯一的区别在于如何从指令中指定的地址(偏移量)获取物理地址:

  • 在实模式下,物理地址基本就是(segment_reg << 4) + offset.

  • 保护模式下,物理地址为translate_via_page_table([segment_reg] + offset).

[segment_reg] 是指段的基地址,在 Global or Local Descriptor Table 中查找 segment_reg 中的偏移量。 translate_via_page_table() 表示通过分页完成地址转换(如果启用)。

正在查找 here, it seems the BIOS ROM appears at physical addresses 0x000F0000-0x000FFFFF. To get at that memory in protected mode with paging, you would have to map it into the virtual address space somewhere by setting up correct page table 个条目。假设 4 KB 页(通常情况),映射整个范围应该需要 16 ((0xFFFFF-0xF0000+1)/4096) 个条目。

要了解 Linux 内核如何做事,您可以查看例如/dev/mem实现了允许读取任意物理地址。实现在drivers/char/mem.c.

以下命令(来自 this answer)将转储内存范围 0xC0000-0xFFFFF(这意味着它也包括视频 BIOS,根据上面链接的内存映射):

$ dd if=/dev/mem bs=1k skip=768 count=256 > bios

1024*768 = 0xC0000,和 1024*(768+256) - 1 = 0xFFFFF,这给出了预期的物理内存范围。

稍微跟踪一下,drivers/char/mem.c 中的 read_mem() 调用 xlate_dev_mem_ptr(),它在 [= 中具有特定于 x86 的实现45=]arch/x86/mm/ioremap.c。如果需要,该函数中的 ioremap_cache() 调用似乎负责页面中的映射。

请注意,顺便说一句,BIOS 例程不会在保护模式下工作。他们假设 CPU 在实模式下是 运行。

对于 Linux x86 32 位,物理 RAM 的第一个 896MB 映射到从虚拟地址 0xC00000000xF7FFFFFF 开始的连续虚拟内存块。从 0xF80000000xFFFFFFFF 的虚拟地址被动态分配给物理内存的各个部分,因此内核可以将 128MB 的 window 映射到超出物理内存的任何部分896MB 限制。

内核本身加载到 1MB 及以上的物理地址,第一个 MB 空闲。例如,第一个 MB 用于拥有 ISA 设备需要的 DMA 缓冲区,因为它们使用 8237 DMA 控制器,只能映射到此类地址。

因此,从虚拟内存地址 0xC0000000 读取实际上是从物理地址 0x00000000 读取(前提是内核已将该页面标记为存在)