QEMU 固件配置(fw_cfg)设备

客户机侧硬件接口

此硬件接口允许客户机取得各种数据项(数据块)。它们可以影响固件自身的配置方式, 也可以包含需要为客户机操作系统安装的表。例如:设备启动顺序、ACPI 和 SMBIOS 表、 虚拟机 UUID、SMP 和 NUMA 信息,以及用于直接启动(Linux)内核的 kernel/initrd 映像等。

选择器(控制)寄存器

  • 只写

  • 位置:取决于平台(IOport 或 MMIO)

  • 宽度:16 位

  • 字节序:小端(IOport)或大端(MMIO)

写入此寄存器可设置固件配置项的索引,随后通过数据寄存器访问该项。

设置选择器寄存器会将数据偏移置零。数据偏移决定通过数据寄存器访问哪些数据, 下文将进一步说明。

选择器寄存器的 Bit14 表示是否写入配置项。值为 0 表示仅读取此项,对数据端口的 所有写访问都被忽略。值为 1 表示可通过写数据寄存器覆盖该项的数据。换言之, 当选择器值位于 0x4000-0x7fff 或 0xc000-0xffff 时,启用配置写模式。

备注

从 QEMU v2.4 开始,不再支持写入 fw_cfg 数据寄存器,写操作将被忽略 (视为空操作)!

备注

从 QEMU v2.9 开始恢复写入支持,但只能通过 DMA 接口(见下文)。 此外,每个具体配置项是否可写独立控制,不受选择器键值中 Bit14 的影响。

选择器寄存器的 Bit15 表示配置项是否为架构专用。值为 0 表示通用配置项, 值为 1 表示特定架构专用的配置项。换言之,选择器值在 0x0000-0x7fff 之间时 访问通用配置项,在 0x8000-0xffff 之间时访问架构专用配置项。

数据寄存器

  • 读/写(从 QEMU v2.4 开始忽略写入,但请参阅 DMA 接口)

  • 位置:取决于平台(IOport[1] 或 MMIO)

  • 宽度:8 位(IOport),8/16/32/64 位(MMIO)

  • 字节序:保持字符串顺序

数据寄存器用于访问各个固件配置数据项的字节数组。具体配置项通过写选择器寄存器 选定,如上文所述。

写入选择器寄存器后,数据偏移最初置零。每次成功访问数据寄存器后,数据偏移按 相应访问宽度递增。

每个固件配置项都有对应的最大数据长度。数据偏移越过此最大长度末尾后, 所有读取均返回 0x00,所有写入均被忽略。

对数据寄存器执行 N 字节宽的读取,将按地址递增顺序,以子字符串形式返回选定 固件配置项的后续 N 个可用字节,与 memcpy() 类似。

寄存器位置

内存映射的 fw_cfg 设备采用以下标准寄存器布局:

  • 基地址:数据寄存器(64 位)

  • 基地址 + 8:选择器寄存器(16 位)

  • 基地址 + 16:DMA 地址寄存器(64 位)

出于历史原因,一些架构或机器采用不同布局:

x86, x86_64
  • 选择器寄存器 IOport:0x510

  • 数据寄存器 IOport: 0x511

  • DMA 地址 IOport: 0x514

PA-RISC:
  • 基地址:选择器寄存器(16 位)

  • 基地址 + 4:数据寄存器(8 位)

32 位 SPARC、PPC g3beige、mac99、prep:
  • 基地址:选择器寄存器(16 位)

  • 基地址 + 2:数据寄存器(8 位)

64 位 SPARC:
  • 基地址:选择器寄存器(16 位)

  • 基地址 + 1:数据寄存器(8 位)

MIPS loongson3-virt 机器:
  • 基地址:选择器寄存器(16 位)

  • 基地址 + 8:数据寄存器(64 位)

ACPI 接口

fw_cfg 设备使用 ACPI ID QEMU0002 定义。由于预期通过 fw_cfg 设备本身将 ACPI 表 传递给客户机,客户机侧固件不能使用 ACPI 查找 fw_cfg。不过,固件设置好 ACPI 表并将 控制权交给客户机内核后,内核可以使用 fw_cfg ACPI 节点,更准确地统计正在使用的 IOport 或 MMIO 区域。

固件配置项

签名(键 0x0000,FW_CFG_SIGNATURE)

使用键 0x0000(FW_CFG_SIGNATURE)选择“签名”项,然后从数据寄存器读取四个字节, 即可验证 fw_cfg 选择器和数据寄存器是否存在。如果存在 fw_cfg 设备,读出的四个字节 将包含字符 QEMU。

如果 DMA 接口可用,读取 DMA 地址寄存器会返回 0x51454d5520434647 (大端格式的 QEMU CFG)。

修订版本/功能位图(键 0x0001,FW_CFG_ID)

该项是一个 32 位小端无符号整数,用于检查已启用的功能。

  • 位 0:传统接口。始终置位。

  • 位 1:DMA 接口。

文件目录(键 0x0019,FW_CFG_FILE_DIR)

存储于选择器键 0x0020 或更高值(FW_CFG_FILE_FIRST 或更高)下的固件配置项, 在目录结构中拥有对应条目,便于客户机侧固件识别并取得这些配置项。 此文件目录的格式(来自 QEMU 源码树的 fw_cfg.h)如下,并附有少量注释以便理解:

struct FWCfgFiles {         /* the entire file directory fw_cfg item */
    uint32_t count;         /* number of entries, in big-endian format */
    struct FWCfgFile f[];   /* array of file entries, see below */
};

struct FWCfgFile {          /* an individual file entry, 64 bytes total */
    uint32_t size;          /* size of referenced fw_cfg item, big-endian */
    uint16_t select;        /* selector key of fw_cfg item, big-endian */
    uint16_t reserved;
    char name[56];          /* fw_cfg item name, NUL-terminated ascii */
};

其他所有数据项

选择器键及其对应配置项的用途、格式和可写性,请以 QEMU 源码中的最新权威列表为准。

范围

理论上,最多可有 0x4000 个通用固件配置项,以及 0x4000 个架构专用配置项。

选择器寄存器

范围用途

0x0000 - 0x3fff

通用(0x0000 - 0x3fff,通常只读,在 QEMU v2.9+ 中可能通过 DMA 接口读写)

0x4000 - 0x7fff

通用(0x0000 - 0x3fff,可读写,在 QEMU v2.4+ 中忽略)

0x8000 - 0xbfff

架构专用(0x0000 - 0x3fff,通常只读,在 QEMU v2.9+ 中可能通过 DMA 接口读写)

0xc000 - 0xffff

架构专用(0x0000 - 0x3fff,可读写,在 v2.4+ 中忽略)

实际允许的固件配置项数量取决于机器类型和版本。

客户机侧 DMA 接口

如果功能位图的位 1 置位,则存在 DMA 接口。它是对现有 fw_cfg 接口的补充,而非替代。 可以通过 64 位宽的地址寄存器使用此接口。

地址寄存器采用大端格式,启动时及操作完成后其值均为 0。写入低半部(偏移 4)会 触发操作。因此,32 位地址的操作只需写入一次即可触发;64 位地址的操作可通过一次 64 位写入,或从高半部(偏移 0)开始的两次 32 位写入触发。

应向该寄存器写入 RAM 中 FWCfgDmaAccess 结构的物理地址。 FWCfgDmaAccess 结构格式如下:

typedef struct FWCfgDmaAccess {
    uint32_t control;
    uint32_t length;
    uint64_t address;
} FWCfgDmaAccess;

结构字段均采用大端格式,位于最低地址的字段为 control。

control 字段包含以下位:

  • 位 0:错误

  • 位 1:读取

  • 位 2:跳过

  • 位 3:选择。高 16 位为选定索引。

  • 位 4:写入

触发操作时,如果 control 字段的位 3 置位,高 16 位会被解释为固件配置项的索引。 效果与写入选择器寄存器相同。

如果 control 字段的位 1 置位,则执行读取操作。将当前选择器与偏移处的 length 字节复制到 address 字段指定的 RAM 物理地址。

如果 control 字段的位 4 置位(且位 1 未置位),则执行写入操作。 将 address 字段指定的 RAM 物理地址处的 length 字节复制到当前选择器与偏移处。 QEMU 不允许写入起点或终点超出当前选择器对应配置项的末尾(即不能调整配置项大小)。 会被截断的写入将整体丢弃。对只读配置项的写入也会被拒绝。所有这些写入错误均会 将 control 字段的位 0(错误位)置位。

如果 control 字段的位 2 置位(且位 1 和位 4 均未置位),则执行跳过操作。 当前选择器的数据偏移前移 length 字节。

读取 control 字段以检查结果:

错误位置位

发生错误。

所有位均清零

传输成功完成。

其他情况

传输仍在进行中(目前实现并非异步,因此不会出现;将来可能出现)。

外部提供的配置项

从 v2.4 开始,可通过 QEMU 命令行插入“文件”fw_cfg 配置项,即选择器键高于 FW_CFG_FILE_FIRST、且在 fw_cfg 文件目录结构中有对应条目的配置项。 语法如下:

-fw_cfg [name=]<item_name>,file=<path>

或:

-fw_cfg [name=]<item_name>,string=<string>

从 v5.1 开始,QEMU 允许一些对象生成 fw_cfg 专用内容,再通过命令行中的 'gen_id' 选项将内容与一个“文件”项关联,语法如下:

-object <generator-type>,id=<generated_id>,[generator-specific-options] \
-fw_cfg [name=]<item_name>,gen_id=<generated_id>

更多文档请参阅 QEMU 手册页。

建议 item_name 仅使用普通 ASCII 字符。

以 opt/ 开头的配置项名称保留给用户。除非用户明确要求,否则 QEMU 绝不会 创建此类名称的条目。

为避免不同用户之间的名称冲突,强烈建议使用以 opt/RFQDN/ 开头的名称, 其中 RFQDN 为你所控制的完全限定域名的反向形式。例如,如果 SeaBIOS 要定义 额外名称,合适的前缀为 opt/org.seabios/。

出于历史原因,opt/ovmf/ 保留给 OVMF 固件。

前缀 opt/org.qemu/ 保留给 QEMU 本身。

使用不以 opt/ 开头的名称可能存在风险,且完全不受支持。尝试使用时 QEMU 会警告。

使用 'gen_id' 时允许名称不以 opt/ 开头(即不发出警告),但必须清楚了解其后果。

所有外部提供的 fw_cfg 配置项对客户机均为只读。