QEMU 与 ACPI BIOS NVDIMM 接口

QEMU 通过 ACPI 支持 NVDIMM。本文描述其基本概念及 QEMU 与 ACPI BIOS 的接口。

NVDIMM ACPI 背景

ACPI 6.0 引入 NVDIMM,在 _SB 作用域定义根设备,_HID 为 ACPI0012。 对每个已存在或平台计划支持的 NVDIMM,固件在根设备下暴露 ACPI 命名空间设备。

子设备的 _ADR 对应 NFIT 设备句柄。根设备和各 NVDIMM 可具有 _DSM, 提供具体实现专用的额外功能。

以下 ACPI 6.0 示例的平台含一个 NVDIMM:

Scope (\_SB){
   Device (NVDR) // Root device
   {
      Name (_HID, "ACPI0012")
      Method (_STA) {...}
      Method (_FIT) {...}
      Method (_DSM, ...) {...}
      Device (NVD)
      {
         Name(_ADR, h) //where h is NFIT Device Handle for this NVDIMM
         Method (_DSM, ...) {...}
      }
   }
}

根设备与 NVDIMM 都支持的方法

_DSM(设备专用方法)

允许设备提供驱动使用的专用控制功能。 NVDIMM DSM 规范见 http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf。

参数:

Arg0

包含 UUID 的 16 字节 Buffer。

Arg1

包含修订 ID 的 4 字节 Integer。

Arg2

包含函数索引的 4 字节 Integer。

Arg3

包含 UUID、修订 ID 和函数索引所指定函数参数的 Package。

返回值:

函数索引为 0 时,返回包含函数索引位域的 Buffer。 否则类型和值由 DSM 规范中相应 UUID、修订 ID 和函数索引定义。

NVDIMM 根设备的方法

_FIT(固件接口表)

求值为 Buffer,数据格式为一系列 NFIT 类型结构。

参数:无。

返回:包含 NFIT 类型结构条目列表的 Buffer。

结构详情见 ACPI 6.0 第 5.2.25 节,NVDIMM Firmware Interface Table(NFIT)。

QEMU NVDIMM 实现

QEMU 使用从 0x0a18 开始的 4 字节 I/O 端口及一个 RAM 内存页实现 NVDIMM ACPI。

内存:

通过 BIOS 链接/加载功能要求 BIOS 分配一页内存, 将地址动态修补到 ACPI 中名为 MEMA 的 int32 对象。

该 RAM 页在 _DSM 与 QEMU 之间传数据。 ACPI 持有控制权时拥有此页,写入输入数据;否则归 QEMU,仿真 _DSM 并写入输出。

ACPI 写入的输入布局,偏移相对页起始:

[0x0 - 0x3]

4 字节 NVDIMM 设备句柄,完全由 QEMU 内部定义。 [1, 0xFFFF] 表示 NVDIMM,其他值另作保留用途。

保留句柄:

  • 0:名为 NVDR 的根设备。

  • 0x10000:在根设备调用的 QEMU 内部 DSM 函数。

[0x4 - 0x7]

4 字节修订 ID,即 _DSM 的 Arg1。

[0x8 - 0xB]

4 字节函数索引,即 Arg2。

[0xC - 0xFFF]

4084 字节 Arg3。

QEMU 输出布局:

[0x0 - 0x3]

4 字节结果长度。

[0x4 - 0xFFF]

4092 字节 DSM 结果。

I/O 端口 0x0a18 - 0xa1b:

ACPI 将 BIOS 分配的页地址写入此端口,QEMU 随即接管并填入结果。

写入访问:

[0x0a18 - 0xa1b]

4 字节 BIOS 分配的内存页地址。

_DSM 处理流程图

MEMA 表示 BIOS 分配的内存页地址。

+----------------------+      +-----------------------+
|    1. OSPM           |      |    2. OSPM            |
| save _DSM input data |      |  write "MEMA" to      | Exit to QEMU
| to the page          +----->|  IO port 0x0a18       +------------+
| indicated by "MEMA"  |      |                       |            |
+----------------------+      +-----------------------+            |
                                                                   |
                                                                   v
+--------------------+       +-----------+      +------------------+--------+
|      5 QEMU        |       | 4 QEMU    |      |        3. QEMU            |
| write _DSM result  |       |  emulate  |      | get _DSM input data from  |
| to the page        +<------+ _DSM      +<-----+ the page indicated by the |
|                    |       |           |      | value from the IO port    |
+--------+-----------+       +-----------+      +---------------------------+
         |
         | Enter Guest
         |
         v
+--------------------------+      +--------------+
|     6 OSPM               |      |   7 OSPM     |
| result size is returned  |      |  _DSM return |
| by reading  DSM          +----->+              |
| result from the page     |      |              |
+--------------------------+      +--------------+

NVDIMM 热插拔

ACPI BIOS 的 GPE.4 处理程序专门通知操作系统 NVDIMM 热添加事件。

仅供 QEMU 内部使用的 _DSM 函数

读取 FIT

_FIT 通过 _DSM 每次获取一页 QEMU NFIT 结构数据,拼接后作为 _FIT 结果返回。

输入参数:

Arg0

UUID,设为 648B9CF2-CDA1-4312-8AD9-49C4AF32BD62。

Arg1

修订 ID,设为 1。

Arg2

函数索引 0x1。

Arg3

包含以下布局 Buffer 的 Package:

字段

长度

偏移

描述

offset

4

0

要读取的 QEMU NFIT 结构数据偏移。

DSM 内存页输出布局:

字段

长度

偏移

描述

length

4

0

返回数据总长度,包含此头部。

status

4

4

状态码:0x0 成功;0x100 表示 _FIT 尚未完成读取时 NFIT 更新导致错误; 其他值遵循 DSM Spec Rev1 第 3 章。

fit data

可变

8

FIT 数据,仅 status 为 0 时存在。

OSPM 自己维护 FIT 偏移。当前偏移加上返回的 fit data 大小,即下一次读取偏移。 全部读完后返回零大小数据。

若返回 0x100,OSPM 应从偏移 0 重新开始读取 FIT。