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
映射到从虚拟地址 0xC0000000
到 0xF7FFFFFF
开始的连续虚拟内存块。从 0xF8000000
到 0xFFFFFFFF
的虚拟地址被动态分配给物理内存的各个部分,因此内核可以将 128MB
的 window 映射到超出物理内存的任何部分896MB
限制。
内核本身加载到 1MB 及以上的物理地址,第一个 MB 空闲。例如,第一个 MB 用于拥有 ISA 设备需要的 DMA 缓冲区,因为它们使用 8237 DMA 控制器,只能映射到此类地址。
因此,从虚拟内存地址 0xC0000000
读取实际上是从物理地址 0x00000000
读取(前提是内核已将该页面标记为存在)
在 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
映射到从虚拟地址 0xC0000000
到 0xF7FFFFFF
开始的连续虚拟内存块。从 0xF8000000
到 0xFFFFFFFF
的虚拟地址被动态分配给物理内存的各个部分,因此内核可以将 128MB
的 window 映射到超出物理内存的任何部分896MB
限制。
内核本身加载到 1MB 及以上的物理地址,第一个 MB 空闲。例如,第一个 MB 用于拥有 ISA 设备需要的 DMA 缓冲区,因为它们使用 8237 DMA 控制器,只能映射到此类地址。
因此,从虚拟内存地址 0xC0000000
读取实际上是从物理地址 0x00000000
读取(前提是内核已将该页面标记为存在)