谁决定 I/O 映射和内存映射 I/O (x86)
Who Decides Between I/O Mapped and Memory Mapped I/O (x86)
在 x86 架构中,我们使用 I/O 指令,例如 I/O 映射的 I/O 的 IN 和 OUT。据我所知,我们在内存映射 I/O 中使用像 MOV 这样的内存指令。这一切都很好,但是谁来决定使用哪种 I/O 方法呢?如果我想构建自己的设备(外围设备),我可以自由选择是使用 I/O 映射还是内存映射 I/O 与 PC 通信?还是所有设备都必须支持两者?
无法理解是谁决定了用于与设备通信的 I/O 方法。
If I want to build my own device (a peripheral) can I choose freely whether I use I/O mapped or memory mapped I/O to communicate with PC?
什么样的设备?
如果它是旧设备(例如古老的 "PS/2 controller" 或串行端口或并行端口或..)或标准化设备(例如实现 AHCI 或 NVMe 或 xHCI),则它必须符合现有的(正式的或事实上的)规范。
否则(没有必须编译的现有规范);如果它是 USB 设备,则不能使用 IO 端口或 MMIO(它响应串行总线上的请求);如果它是一个 PCI 设备并且需要高性能它应该使用 MMIO(因为 IO 端口是一个性能问题);如果它是不需要高性能的 PCI 设备,则根本不应该是 PCI 设备(应该是 USB)。
由于它是制造商,它并不总是有充分的自由。
标准和规范可以强制使用地址 space,一些标准是通用的(例如 OHCI、USB 1.0,指的是 "uncachable address space",在 x86 上可以是 IO 或 MMIO)其他不是(例如,PC 客户端 TPM 规范根据所使用的 MMIO 区域按位置映射 TPM 寄存器)。
据我所知,就我们在这个答案中所关注的而言,随着 PCI1.
的出现,MMIO 的采用成为主流
PCI BAR(基地址寄存器)有一种特殊的格式,允许软件知道卡正在使用哪个地址space(以及需要多少地址):
Bit 0 是 Read-Only(由制造商设置)并告诉卡正在使用哪个地址 space。
IO space 优于 MMIO,不需要任何设置,MMIO 需要虚拟到物理映射和正确的缓存类型。
然而 IO space 只有 64KiB + 3B,非常 小。
事实上,PCI 2.2 将单个 BAR 使用的最大 IO space 限制为 256 字节。
对不起图片,从 PDF 规范复制给我乱码
此外,指针在 IO space 中不起作用,某些设备使用指针(例如 USB 控制器、GBe 等)。
IO 肯定用于旧设备(在 MMIO 出现之前)。
我曾经认为IO用于具有少量寄存器的设备但并不总是如此,例如PCH(芯片组)的Power Manager控制寄存器是IO映射的并且占用128B。
有时,设备同时支持IO 和MMIO。这需要两个 BAR,例如 PCH 的 SMBus 控制器:
它有两个 BAR(注意默认值,一个用于 IO,另一个用于 MMIO)控制着 相同 组寄存器。
文档指定两者都可以使用。
我无法给出何时使用 IO 与 MMIO 的确切规则。
我认为性能上没有区别,区别只是 PCIe link 层发送的 TLP 数据包中的一点点。
但是我从来没有调查过这个问题,IO 指令是序列化的,因此在软件级别存在性能损失。
我的经验是,如果满足以下任一条件,则使用 IO is/can:
- 该设备是旧设备(这里确实没有选择的自由)。
- 您的设备没有使用指针(因为 IO 没有指针)并且寄存器集很小。
- 寄存器主要用于控制和报告设备或整个系统的状态(因为IO指令是序列化的)。
这些只是经验之谈,根据我的阅读和记忆,还有很多例外和反例。
今天的趋势是使用 MMIO,这可能需要 更多 解码逻辑(更多地址线要解码),但 PCI 规范通过允许设备将其解码四舍五入到 4KiB 来简化它。
一个例子是 PCIe 配置 space,在 PCI 中它是 IO 访问的(使用类似于寄存器堆栈的技术,例如 VGA 控制器)但现在是内存映射的。
无需考虑其他总线,因为 PCIe 是现代 PC 上的主要总线,其他一切都通过 PCIe 设备(例如 USB 使用 xHCI PCI 设备)。
唯一的例外是 off-core 设备(例如 LAPIC、TXT 寄存器),这些是通过内存映射 IO 访问的,因为我认为它的性能更高,这种访问不会进入系统代理(这些设备靠近它们的核心并且无论如何都在 CPU 包内)因此使用(序列化)IO 指令会对它们产生重大影响。
此外,在 4GiB 的顶部有一个不错的位置,英特尔可以在其中回收内存而不会对其他设备造成太大压力。
有趣的事实:由于 FPU 是协处理器 (x87) 的时代,端口 0xf8-0xff 被保留并且该端口被 CPU 用于与其通信。
1 在此之前,其他 PnP buses was already available (e.g. PnP ISA 和 MCA) 但解码内存访问主要是为了提供对 ROM 和 on-card RAM 的访问。将寄存器映射到内存还不是我想的事情。
在 x86 架构中,我们使用 I/O 指令,例如 I/O 映射的 I/O 的 IN 和 OUT。据我所知,我们在内存映射 I/O 中使用像 MOV 这样的内存指令。这一切都很好,但是谁来决定使用哪种 I/O 方法呢?如果我想构建自己的设备(外围设备),我可以自由选择是使用 I/O 映射还是内存映射 I/O 与 PC 通信?还是所有设备都必须支持两者?
无法理解是谁决定了用于与设备通信的 I/O 方法。
If I want to build my own device (a peripheral) can I choose freely whether I use I/O mapped or memory mapped I/O to communicate with PC?
什么样的设备?
如果它是旧设备(例如古老的 "PS/2 controller" 或串行端口或并行端口或..)或标准化设备(例如实现 AHCI 或 NVMe 或 xHCI),则它必须符合现有的(正式的或事实上的)规范。
否则(没有必须编译的现有规范);如果它是 USB 设备,则不能使用 IO 端口或 MMIO(它响应串行总线上的请求);如果它是一个 PCI 设备并且需要高性能它应该使用 MMIO(因为 IO 端口是一个性能问题);如果它是不需要高性能的 PCI 设备,则根本不应该是 PCI 设备(应该是 USB)。
由于
标准和规范可以强制使用地址 space,一些标准是通用的(例如 OHCI、USB 1.0,指的是 "uncachable address space",在 x86 上可以是 IO 或 MMIO)其他不是(例如,PC 客户端 TPM 规范根据所使用的 MMIO 区域按位置映射 TPM 寄存器)。
据我所知,就我们在这个答案中所关注的而言,随着 PCI1.
的出现,MMIO 的采用成为主流
PCI BAR(基地址寄存器)有一种特殊的格式,允许软件知道卡正在使用哪个地址space(以及需要多少地址):
Bit 0 是 Read-Only(由制造商设置)并告诉卡正在使用哪个地址 space。
IO space 优于 MMIO,不需要任何设置,MMIO 需要虚拟到物理映射和正确的缓存类型。
然而 IO space 只有 64KiB + 3B,非常 小。
事实上,PCI 2.2 将单个 BAR 使用的最大 IO space 限制为 256 字节。
对不起图片,从 PDF 规范复制给我乱码
此外,指针在 IO space 中不起作用,某些设备使用指针(例如 USB 控制器、GBe 等)。
IO 肯定用于旧设备(在 MMIO 出现之前)。
我曾经认为IO用于具有少量寄存器的设备但并不总是如此,例如PCH(芯片组)的Power Manager控制寄存器是IO映射的并且占用128B。
有时,设备同时支持IO 和MMIO。这需要两个 BAR,例如 PCH 的 SMBus 控制器:
它有两个 BAR(注意默认值,一个用于 IO,另一个用于 MMIO)控制着 相同 组寄存器。
文档指定两者都可以使用。
我无法给出何时使用 IO 与 MMIO 的确切规则。
我认为性能上没有区别,区别只是 PCIe link 层发送的 TLP 数据包中的一点点。
但是我从来没有调查过这个问题,IO 指令是序列化的,因此在软件级别存在性能损失。
我的经验是,如果满足以下任一条件,则使用 IO is/can:
- 该设备是旧设备(这里确实没有选择的自由)。
- 您的设备没有使用指针(因为 IO 没有指针)并且寄存器集很小。
- 寄存器主要用于控制和报告设备或整个系统的状态(因为IO指令是序列化的)。
这些只是经验之谈,根据我的阅读和记忆,还有很多例外和反例。
今天的趋势是使用 MMIO,这可能需要 更多 解码逻辑(更多地址线要解码),但 PCI 规范通过允许设备将其解码四舍五入到 4KiB 来简化它。
一个例子是 PCIe 配置 space,在 PCI 中它是 IO 访问的(使用类似于寄存器堆栈的技术,例如 VGA 控制器)但现在是内存映射的。
无需考虑其他总线,因为 PCIe 是现代 PC 上的主要总线,其他一切都通过 PCIe 设备(例如 USB 使用 xHCI PCI 设备)。
唯一的例外是 off-core 设备(例如 LAPIC、TXT 寄存器),这些是通过内存映射 IO 访问的,因为我认为它的性能更高,这种访问不会进入系统代理(这些设备靠近它们的核心并且无论如何都在 CPU 包内)因此使用(序列化)IO 指令会对它们产生重大影响。
此外,在 4GiB 的顶部有一个不错的位置,英特尔可以在其中回收内存而不会对其他设备造成太大压力。
有趣的事实:由于 FPU 是协处理器 (x87) 的时代,端口 0xf8-0xff 被保留并且该端口被 CPU 用于与其通信。
1 在此之前,其他 PnP buses was already available (e.g. PnP ISA 和 MCA) 但解码内存访问主要是为了提供对 ROM 和 on-card RAM 的访问。将寄存器映射到内存还不是我想的事情。