我试图了解 IO 设备如何映射到运行 Linux 的现代 x86 机器上的“常规”内存地址空间。
我试图理解的一些细节是:
cat /proc/iomem
打印出 io 内存映射区域的列表(打印身体的地址)是不连续的这些区域可以在运行时由内核模块动态请求,并通过 <linux/ioport.h> 中定义的函数 request_mem_region 进行分配
x86 机器
mov
同时用于内存访问和 IO(映射到内存)
因此,现在,假设内核模块代码正在运行,我们可能会遇到一条指令,例如mov [value] [virtual address]
虚拟地址可能引用内存中存在的 mmio 区域或“正常”数据值。将 mmio 流量与“正常”内存分离的过程应该有 2 个关键步骤:
- 决定如果地址是mmio。页表有一个标志表明它是否是内存映射的,所以我假设 mmu 在进行页表转换时确定了这一点。
- 确定新生成的物理地址(mmu 作为页表转换的输出)的“IO 地址”,并将其传递到与 IO 接口的任何芯片(北桥、根复合体等)
问题1:我对上述步骤 1 的理解正确吗?
问题2: 步骤2是如何进行的? (通过什么方式实体地图是如何存储的?)
需要检查的范围列在 /proc/iomem 中,我猜它从中提取的数据是一个如下所示的地图:
map[mmio_address] = io_address_object
请记住这一切正在发生之内从CPU的角度来看单个mov
指令的上下文,我看不出这种转换是如何通过CPU外部硬件以外的任何东西发生的。
答案1
您对步骤1的理解是正确的。 MMU 通过查看该虚拟地址的页表条目来确定该地址是否为 MMIO。
对于步骤2,负责将物理地址映射到相应IO地址的实体是主板上的芯片组。芯片组通常包含内存映射 IO (MMIO) 控制器,负责处理来自 CPU 的 IO 请求,并将其转换为连接到系统的各种 IO 设备可以理解的信号。
MMIO 控制器通常维护一个存储器映射,将每个 MMIO 地址范围映射到每个 IO 设备的相应 IO 地址范围。该内存映射通常存储在芯片组内的硬件寄存器中,并在系统初始化期间由 BIOS 进行编程。
当CPU执行MMIO操作时,MMU将虚拟地址转换为物理地址,然后将其发送到MMIO控制器。 MMIO控制器使用内存映射来确定物理地址对应的IO地址,并生成适当的IO信号来与IO设备进行通信。
所以,综上所述,MMIO地址和IO地址之间的映射是由芯片组中的MMIO控制器维护的,并且该映射是在系统初始化期间由BIOS编程的。