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

x86平台上运行的Linux中,实模式地址空间是在受保护的内核模式下映射到的吗? 在内核模式下,一个线程可以直接访问内核地址空间。 内核处于低8MB,页表处于某个位置等(如此处所述)。 但是实模式地址空间在哪里呢? 可以直接访问吗? 例如BIOS和BIOS插件(见这里 )?

(我的x86-fu有点弱,我会添加一些标签,以便别人可以(希望)纠正我,如果我躺在任何地方)。

物理地址在真实和保护模式下是相同的。 唯一的区别是你如何从指令中指定的地址(偏移量)到物理地址:

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

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

通过[segment_reg]我的意思是段的基地址,在全局或本地描述符表中查找segment_reg中的偏移量。 translate_via_page_table()表示通过分页完成地址转换(如果启用)。

看这里 ,看起来BIOS ROM出现在物理地址0x000F0000-0x000FFFFF。 为了在分页的保护模式下获得内存,你必须通过设置正确的页表项来将它映射到虚拟地址空间。 假设4 KB页(通常情况下),映射整个范围应该需要16((0xFFFFF-0xF0000 + 1)/ 4096)条目。

要了解Linux内核如何执行操作,可以查看如何实现允许读取任意物理地址的eg /dev/mem 。 实现在drivers / char / mem.c中

下面的命令(例如从这个答案 )将转储内存范围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中调用xlate_dev_mem_ptr() ,在arch / x86 / mm / ioremap.c中有一个特定于x86的实现。 如果需要,该函数中的ioremap_cache()调用似乎负责在页面中进行映射。

请注意,BIOS例程不能在保护模式下工作。 他们假设CPU正在实模式下运行。

对于Linux x86 32位,首先将896MB物理RAM映射到从虚拟地址0xC00000000xF7FFFFFF开始的连续虚拟内存块。 从0xF80000000xFFFFFFFF虚拟地址是动态分配给物理内存的各个部分的,所以内核可以有一个128MB的窗口映射到超过896MB限制的物理内存的任何部分。

内核本身的物理地址为1MB或更多,第一个MB空闲。 例如,使用这个第一个MB来具有ISA设备需要的DMA缓冲区,因为它们使用8237 DMA控制器,它只能映射到这些地址。

所以,从虚拟内存地址0xC0000000读取实际上是从物理地址0x00000000读取(如果内核已经标记该页面存在)