ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

QEMU ACPI ERST 设备详解:ACPI 错误记录序列化与持久化存储实现

QEMU ACPI ERST 设备详解:ACPI 错误记录序列化与持久化存储实现 QEMU ACPI ERST 设备详解ACPI 错误记录序列化与持久化存储实现【免费下载链接】qemuOfficial QEMU mirror. Please see https://www.qemu.org/contribute/ for how to submit changes to QEMU. Pull Requests are disabled. Please only use release tarballs from the QEMU website.项目地址: https://gitcode.com/gh_mirrors/qe/qemu本篇技术指南围绕 QEMU 对 ACPI 4.0 规范中 ACPI Platform Error Interfaces (APEI) 章节所定义的 Error Record Serialization TableERST功能的实现展开讲解acpi-erstPCI 设备的设计动机、QEMU 命令行配置方法、ACTION/VALUE 寄存器编程接口以及后端持久化存储的二进制布局。读完本文你将掌握如何在 QEMU 虚拟机上为 Linux pstore 提供可用的 ACPI ERST 后端、理解 ERST 设备与memory-backend-file的配合方式并能从源码层面读懂其记录读写与清除的实现原理。什么是 ACPI ERSTACPI ERSTError Record Serialization Table错误记录序列化表是 ACPI 4.0 规范在 ACPI Platform Error Interfaces (APEI) 一章 Error Serialization 小节中定义的一种机制用于把错误记录持久化保存到非易失性存储中供事后排查与调试使用见 docs/specs/acpi_erst.rst。错误记录的格式由 UEFI 规范附录 N Common Platform Error RecordCPER定义。QEMU 中的acpi-erst设备正是为了在虚拟化环境中提供这一能力而实现的其源码位于 hw/acpi/erst.c配套的构建条件CONFIG_ACPI_ERST在 hw/acpi/meson.build 中控制acpi_ss.add(when: CONFIG_ACPI_ERST, if_true: files(erst.c))设备类型定义在 include/hw/acpi/erst.h#define TYPE_ACPI_ERST acpi-erstNVRAM 模式与非 NVRAM 模式ACPI 规范允许两种错误日志存储模式NVRAM 模式直接把非易失性 RAM 暴露给 OS/guest 进行直接访问由GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES动作的属性位描述非 NVRAM 模式通过寄存器编程接口进行读写因为闪存更新本身需要编程操作。QEMU 的acpi-erst实现的是非 NVRAM 模式。这也是绝大多数 BIOS 采用的实现方式且截至当前Linux 仅支持非 NVRAM 模式。在源码中ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES处理时显式返回0x0即不声明 NVRAM 属性hw/acpi/erst.ccase ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES: s-reg_value 0x0; /* intentional, not NVRAM mode */ break;背景与动机为 pstore 提供持久化后端Linux 使用持久化存储文件系统pstore在系统 panic 或关机时记录信息例如 dmesg 的尾部内容。pstore 独立于 kdump 且在 kdump 之前运行。在某些场景下——例如宿主/guest 的根文件系统位于 NFS/iSCSI 上而网络软件或硬件恰好失效、导致 kdump 无法工作时——pstore 中保存的信息就成为事后调试的唯一依据。pstore 常见的两个存储后端是ACPI ERST和UEFI。大多数 BIOS 实现了 ACPI ERST而 UEFI 并非所有 guest 都启用。因此 QEMU 支持 ACPI ERST 后虚拟机如同裸机一样就多了一个可行的 pstore 存储后端。其价值在于在从资源受限的 microvm 到大型 guest 的广泛场景中提供一致的内核 panic 信息捕获手段特别适用于直接引导direct-boot环境——这种环境缺少 UEFI 运行时服务无法使用 UEFI 作为 pstore 后端Microsoft Windows 在可用时也会利用 ACPI ERST 保存某些崩溃信息。配置与使用命令行示例要启用 ACPI ERST需要同时创建一个memory-backend-file对象和acpi-erst设备qemu ... -object memory-backend-file,iderstnvram,mem-pathacpi-erst.backing,size0x10000,shareon \ -device acpi-erst,memdeverstnvrammemory-backend-file 参数acpi-erst设备正确工作所需的memory-backend-file参数如下参数说明id该内存后端对象的 id用于与acpi-erst设备关联sizeACPI ERST 后端存储的大小必填mem-pathACPI ERST 后端存储文件的位置必填share必须设置为on确保对 ERST 后端存储的更新会写回该文件在源码层面erst_realizefn()hw/acpi/erst.c会对这些约束做强制校验memdev属性未设置会直接报错并中止 realize后端存储大小取自HostMemoryBackend的size属性且必须是页大小的整数倍。acpi-erst 设备参数参数说明memdev即memory-backend-file对象的 idrecord_size后端存储中每个记录槽位的大小必须是大于等于 4096PAGE_SIZE的 2 的幂record_size在设备属性中定义hw/acpi/erst.c默认值为ERST_RECORD_SIZE即 81928KiBstatic const Property erst_properties[] { DEFINE_PROP_LINK(ACPI_ERST_MEMDEV_PROP, ERSTDeviceState, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_UINT32(ACPI_ERST_RECORD_SIZE_PROP, ERSTDeviceState, default_record_size, ERST_RECORD_SIZE), };后端存储合法性检查在check_erst_backend_storage()hw/acpi/erst.c中进行校验规则包括record_size非零、不小于 UEFI CPER 记录最小尺寸 128 字节、必须是 2 的幂、且不小于 4096后端存储总大小必须是record_size的整数倍若后端是首次使用magic 全零因为HostMemoryBackend会把内容初始化为零会自动生成存储头make_erst_storage_header()hw/acpi/erst.c头中 magic、version、storage_offset 对齐等字段必须合法。PCI 接口acpi-erst是一个 PCI 设备包含两个 BARBAR0编程寄存器接口包含 ACTION 和 VALUE 两个 64 位寄存器BAR18KiB 记录交换缓冲区record exchange buffer即本实现支持的最大记录尺寸。PCI 设备信息在erst_class_init()hw/acpi/erst.c中注册vendor id 为 Red Hatdevice id 为PCI_DEVICE_ID_REDHAT_ACPI_ERST0x0012见 include/hw/pci/pci.hclass 为PCI_CLASS_OTHERS不可热插拔但用户可通过命令行创建。ACTION / VALUE 编程模型所有 ERST 动作、操作及其副作用都发生在写入 ACTION 寄存器这一时刻这是该实现的刻意设计。任何动作所需的数据必须先写入 VALUE 寄存器再写 ACTION 寄存器触发。读取 VALUE 寄存器只返回寄存器当前内容——它可能被先前的 ACTION 更新过。编程寄存器区的布局hw/acpi/erst.c#define ERST_REG_SIZE (16UL) #define ERST_ACTION_OFFSET (0UL) /* action (cmd) */ #define ERST_VALUE_OFFSET (8UL) /* argument/value (data) */寄存器读写处理函数erst_reg_write()/erst_reg_read()hw/acpi/erst.c支持 32 位与 64 位访问32 位访问时按地址低位的0x4位选择高/低 32 位。注意 ACTION 寄存器只需 32 位即可所有合法的 ACTION 值都是 32 位范围内的因此ERST_ACTION_OFFSET 4未被使用。串行化动作Serialization Actions源码根据 ACPI 4.0 Table 17-16 定义了全部串行化动作hw/acpi/erst.c动作值宏名含义0x0ACTION_BEGIN_WRITE_OPERATION开始写操作0x1ACTION_BEGIN_READ_OPERATION开始读操作0x2ACTION_BEGIN_CLEAR_OPERATION开始清除操作0x3ACTION_END_OPERATION结束操作0x4ACTION_SET_RECORD_OFFSET设置记录偏移0x5ACTION_EXECUTE_OPERATION执行操作0x6ACTION_CHECK_BUSY_STATUS检查忙状态0x7ACTION_GET_COMMAND_STATUS获取命令状态0x8ACTION_GET_RECORD_IDENTIFIER获取记录标识符0x9ACTION_SET_RECORD_IDENTIFIER设置记录标识符0xAACTION_GET_RECORD_COUNT获取记录数量0xBACTION_BEGIN_DUMMY_WRITE_OPERATION开始伪写操作0xCACTION_RESERVED保留0xDACTION_GET_ERROR_LOG_ADDRESS_RANGE获取错误日志地址范围0xEACTION_GET_ERROR_LOG_ADDRESS_LENGTH获取错误日志地址长度0xFACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES获取地址范围属性0x10ACTION_GET_EXECUTE_OPERATION_TIMINGS获取执行操作时序ACPI 6.3 新增一个典型的读操作时序是写 VALUE 设置 record identifier → 写 ACTION(ACTION_SET_RECORD_IDENTIFIER) → 写 ACTION(ACTION_BEGIN_READ_OPERATION) → 写 VALUE 为0x9C→ 写 ACTION(ACTION_EXECUTE_OPERATION) → 写 ACTION(ACTION_CHECK_BUSY_STATUS) 读 VALUE 判断忙 → 写 ACTION(ACTION_GET_COMMAND_STATUS) 读 VALUE 取状态 → 从 BAR1 读取记录。其中ACTION_EXECUTE_OPERATION要求 VALUE 中写入实现特定的魔数0x9CERST_EXECUTE_OPERATION_MAGIC见 hw/acpi/erst.c只有魔数正确才真正执行已选定的操作。命令状态源码根据 ACPI 4.0 Table 17-17 定义了命令状态码hw/acpi/erst.c状态值宏名含义0x00STATUS_SUCCESS成功0x01STATUS_NOT_ENOUGH_SPACE存储空间不足0x02STATUS_HARDWARE_NOT_AVAILABLE硬件不可用0x03STATUS_FAILED失败0x04STATUS_RECORD_STORE_EMPTY记录存储为空0x05STATUS_RECORD_NOT_FOUND记录未找到ACPI 表生成build_erst()hw/acpi/erst.c负责生成 ERST ACPI 表即 Serialization Action Table。它会为上述每个动作构建 Serialization Instruction Entry每个条目 32 字节描述固件执行该动作时应执行的寄存器读写指令序列如INST_WRITE_REGISTER_VALUE写 ACTION、INST_READ_REGISTER_VALUE读 VALUE 等并通过acpi_table_begin/acpi_table_end与 BIOS linker 一起输出。后端存储格式后端存储被划分为固定大小的 slot槽位每个 slot 长度为 8KiB即record_size每个 slot 存放一条记录。并非所有 slot 都必须被占用也不要求连续占用通过清除/擦除特定记录即可形成空槽位。布局示例对于小于 8MiB 的存储存储总大小是 8KiB 的整数倍包含 N 个用于存放记录的 slot。下面是一个示例布局其中两条 CPER 记录已存入其余 slot 为空可用Slot Record ------------------ 8KiB -------------------- -------------------------------------------- 0 | storage header | -------------------------------------------- 1 | empty/available | -------------------------------------------- 2 | CPER | -------------------------------------------- 3 | CPER | -------------------------------------------- ... | | -------------------------------------------- N | empty/available | --------------------------------------------Slot 0 存储头与多槽头Slot 0 包含后端存储头它标识内容为 ERST 存储并提供对记录的高效访问。根据后端存储大小可能会有额外的 slot 被划入 slot 0 头。例如在 8KiB 槽位大小下slot 0 头可容纳 1021 条记录的 record_id。因此当存储大小为 8MiB8KiB × 1024时需要额外一个 slot 来扩展头此时 slot 0 与 slot 1 共同构成后端存储头记录从 slot 2 开始存放。从源码看ERSTStorageHeader结构定义如下hw/acpi/erst.ctypedef struct { uint64_t magic; uint32_t record_size; uint32_t storage_offset; /* offset to record storage beyond header */ uint16_t version; uint16_t reserved; uint32_t record_count; uint64_t map[]; /* contains record_ids, and position indicates index */ } __attribute__((packed)) ERSTStorageHeader;头中的所有字段均以小端序存储。make_erst_storage_header()在首次初始化时计算storage_offset先按存储总槽位数计算 map 数组大小再整体向上对齐到record_size的整数倍作为记录存储区的起点偏移。头字段说明参考文档 docs/specs/acpi_erst.rst 对头格式的描述按 8KiB 槽位、存储小于 8MiB 的情形-------------------------------------------- | magic | 0x0000 -------------------------------------------- | record_offset | record_size | 0x0008 -------------------------------------------- | record_count | reserved | version | 0x0010 -------------------------------------------- | record_id[0] | 0x0018 -------------------------------------------- | record_id[1] | 0x0020 -------------------------------------------- | record_id[...] | -------------------------------------------- | record_id[N] | 0x1FF8 --------------------------------------------各字段含义magic值为0x524F545354535245ASCII 字符串 ERSTSTOR对应源码ERST_STORE_MAGIC见 hw/acpi/erst.crecord_size值为0x2000即 8KiBrecord_offset指向 record_id 数组中的第一个 record_id即0x0018对应源码中的storage_offset字段当前实现记录存储区起点偏移version值为0x0100即第一个版本record_count后端存储中有效记录的条数record_id[] 数组数组元素是各槽位中 CPER 记录的 64 位记录标识符。换言之record_id 在数组中的位置下标即对应记录在后端存储中的槽位索引。例如存储小于 8MiB 时 slot 0 是头因此record_id[0]永远不会是有效的 CPER record_idslot 1 才是第一个可用槽位record_id[1]可能存有 CPER。record_id 取值约定hw/acpi/erst.c全 0ERST_UNSPECIFIED_RECORD_ID表示未指定全 1ERST_EMPTY_END_RECORD_ID表示空/结束因此record_id为全 0 或全 1 都表示该槽位无效可用。源码实现要点设备状态ERSTDeviceStatehw/acpi/erst.c保存了后端存储指针、编程寄存器区、交换缓冲区以及接口状态当前操作、忙状态、命令状态、记录偏移、寄存器值、记录标识符、头指针、首/末/下一个记录索引等。记录写入write_erst_record()hw/acpi/erst.c实现写入流程校验record_offset不越界须保证交换缓冲区内至少能容纳 CPER 最小记录 128 字节从交换缓冲区读取 CPER 的record_length偏移 20 处并校验合法性从 CPER 记录偏移 96 处提取 64 位record_identifier在 map 中查找该标识符找到则覆盖既有记录未找到则分配下一个空槽位若所有槽位均已占用则返回STATUS_NOT_ENOUGH_SPACE写入记录槽位剩余部分填充0xFF更新record_count与 map 条目返回STATUS_SUCCESS。记录读取read_erst_record()hw/acpi/erst.c实现读取流程存储为空时返回STATUS_RECORD_STORE_EMPTYrecord_id 为全 0 时自动定位到第一条记录校验偏移与长度后把记录从存储槽位拷贝到交换缓冲区。注意读取 CPER 字段时源码特意使用memcpy()以避免宿主机上的非对齐访问。记录清除clear_erst_record()hw/acpi/erst.c实现清除流程无需擦除槽位内容只需把对应 map 条目置为ERST_UNSPECIFIED_RECORD_ID并递减record_count即可这正是通过清除/擦除特定记录形成空槽位的实现方式。记录枚举get_next_record_identifier()hw/acpi/erst.c支持按顺序枚举有效记录扫描到末尾时自动重置到开头。迁移支持设备实现了迁移状态描述erst_vmstatehw/acpi/erst.c迁移操作、忙状态、命令状态、记录偏移、寄存器值与记录标识符等接口状态并在 realize 时通过vmstate_register_ram_global()把后端存储 RAM 纳入迁移流erst_post_load()在迁移恢复后重新计算头指针。这意味着虚拟机迁移后ERST 接口状态与已持久化的错误记录都能保持。复位行为erst_reset()hw/acpi/erst.c在复位时清零操作/忙状态、把命令状态置为成功、记录标识符置为未指定、记录偏移置零并重置下一个记录索引但不会破坏后端存储中的记录数据。测试与验证QEMU 提供了针对acpi-erst的自动化测试tests/qtest/erst-test.c/acpi-erst/basic测试启动带-object memory-backend-file,size64K,shareon,idnvram -device acpi-erst,memdevnvram的虚拟机验证 BAR0 大小为 16 字节、BAR1 大小为0x20008KiB并通过写入 ACTION 0xD/0xE/0xF 依次读取GET_ERROR_LOG_ADDRESS_RANGE、GET_ERROR_LOG_ADDRESS_LENGTH、GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES断言地址范围非 0、非全 1、等于 BAR1 地址地址长度等于 BAR1 大小属性为 0非 NVRAM 模式。tests/qtest/bios-tables-test.cacpi/piix4/acpierst、acpi/q35/acpierst、acpi/microvm/acpierst用例分别在 pci440FX、q35 与 microvm 机器上以size0x10000,shareon的memory-backend-file启动acpi-erst并校验生成的 ACPI 表.acpierst变体。参考规范Advanced Configuration and Power Interface Specificationversion 4.0June 2009——定义了 APEI / Error Serialization 机制。Unified Extensible Firmware Interface Specificationversion 2.1October 2008——附录 N 定义了 Common Platform Error RecordCPER格式。Windows Hardware Error Architecture——Error Record Persistence Mechanism 描述了 Windows 对错误记录持久化机制的使用。上述原始设计文档位于 docs/specs/acpi_erst.rst实现代码位于 hw/acpi/erst.c设备类型声明见 include/hw/acpi/erst.h是进一步阅读与二次开发的最佳起点。【免费下载链接】qemuOfficial QEMU mirror. Please see https://www.qemu.org/contribute/ for how to submit changes to QEMU. Pull Requests are disabled. Please only use release tarballs from the QEMU website.项目地址: https://gitcode.com/gh_mirrors/qe/qemu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表