ARTICLE DETAIL

资讯详情

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

QEMU ACPI CPU 热插拔接口规范深度解析:寄存器协议、AML 固件实现与 OSPM 协作机制

QEMU ACPI CPU 热插拔接口规范深度解析:寄存器协议、AML 固件实现与 OSPM 协作机制 QEMU ACPI CPU 热插拔接口规范深度解析寄存器协议、AML 固件实现与 OSPM 协作机制【免费下载链接】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 官方接口规范文档 docs/specs/acpi_cpu_hotplug.rst 为核心骨架结合仓库内 hw/acpi/cpu.c、include/hw/acpi/cpu.h、include/hw/acpi/pc-hotplug.h 等源码与 tests/qtest/bios-tables-test.c 测试用例系统讲解 QEMU 与 ACPI BIOS 之间用于 CPU 热插拔的现代寄存器接口。读者将掌握寄存器块的布局与读写语义、命令字段的完整协议、OST 状态上报流程以及检测接口 → 获取事件 CPU → 枚举 CPU → 触发 eject四条标准 OSPM 操作路径的精确实现步骤并能对照源码理解每个寄存器位在 QEMU 内部状态的落地方式。一、接口背景QEMU 与 ACPI BIOS 如何协作实现 CPU 热插拔QEMU 通过 ACPI高级配置与电源管理接口向客户机提供 CPU 热插拔CPU hotplug能力。整个交互模型是QEMU 硬件侧寄存器 ACPI BIOS 固件 客户机 OSPM操作系统电源管理三方协作QEMU 侧在 IO 空间中暴露一组固定布局的寄存器块作为设备模型与固件之间的邮箱记录每个 CPU 插槽的插入/移除状态、事件标志与 OSTOSPM Status Table信息ACPI BIOS 侧生成描述这些寄存器的 ACPI 表与 AML 代码由build_cpus_aml()动态构建并实现CSCN扫描、CEJ0弹出、COSTOST 上报等控制方法OSPM 侧通过_STA、_EJ0、_OST等 ACPI 对象查询 CPU 状态、请求弹出并回写操作结果。文档明确指出ACPI BIOS GPE.2 处理器专用于向 OS 通知 CPU hot-add热添加与 hot-remove热移除事件。QEMU 内部对应的事件源定义于 include/hw/acpi/acpi_dev_interface.hACPI_CPU_HOTPLUG_STATUS 4在通用事件设备GED路径中由 hw/acpi/generic_event_device.c 处理。二、现代 CPU 热插拔寄存器块总览2.1 基地址与大小项目值ICH9-LPCq35 机型IO 端口0x0cd8PIIX-PMi440fx/pc 机型IO 端口0xaf00寄存器块长度ACPI_CPU_HOTPLUG_REG_LEN 12字节两个基地址在源码中均有明确宏定义见 include/hw/acpi/pc-hotplug.h#define ICH9_CPU_HOTPLUG_IO_BASE 0x0CD8 #define PIIX4_CPU_HOTPLUG_IO_BASE 0xaf00寄存器块长度宏定义于 include/hw/acpi/cpu.h#define ACPI_CPU_HOTPLUG_REG_LEN 12。QEMU 侧初始化寄存器块的入口是cpu_hotplug_hw_init()以 q35 为例hw/acpi/ich9.c 中将其挂载到 LPC 的 PCI IO 地址空间cpu_hotplug_hw_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), pm-cpuhp_state, ICH9_CPU_HOTPLUG_IO_BASE);2.2 全局访问约定规范对寄存器块的全局行为做出如下约定任何固件实现都必须遵守字节序下述所有寄存器访问均为小端字节序little-endian与cpu_hotplug_ops中声明的DEVICE_LITTLE_ENDIAN一致见 hw/acpi/cpu.c保留寄存器写入被忽略读取返回全 0CPU selector 合法性CPU selector中最后存储的值必须指向一个可能的 CPUpossible CPU即小于max_cpus否则从任意寄存器读取返回 0对其它寄存器的写入被忽略直到向 selector 写入合法值复位语义QEMU 启动时CPU selector被初始化为一个合法值系统复位reset时保持当前值不变。从源码结构看selector 越界检查在每次读/写操作入口完成cpu_hotplug_rd()在cpu_st-selector cpu_st-dev_count时直接返回 0cpu_hotplug_wr()在地址非零且 selector 越界时直接返回见 hw/acpi/cpu.c 与 hw/acpi/cpu.c。三、读访问行为Read Access Behavior读访问通过先写 selector 选中 CPU再读目标偏移的两步流程进行。各偏移语义如下。3.1 offset [0x0-0x3]Command data 2DWORD 访问读取结果取决于Command field中最后存储的值Command field 最后存储值读取结果0返回0x03返回架构特定 CPU ID 值的高 32 位其它保留对应源码 hw/acpi/cpu.c命令CPHP_GET_CPU_ID_CMD值 3时返回cdev-arch_id 32。3.2 offset [0x4]CPU device status fields1 字节访问位语义bit 0Device enabled设备已启用客户机可以使用该 CPUbit 1Device insert event设备插入事件标志用于区分尚未向 OSPM 发出 device check 事件的设备。仅当 bit 0 置位时有效bit 2Device remove event设备移除事件标志用于区分尚未向 OSPM 发出 device eject 请求的设备。固件必须忽略此位bit 3保留OSPM 应忽略bit 4若置 1表示OSPM 请求固件执行设备弹出device ejectbit 5-7保留OSPM 应忽略该状态位在 QEMU 内部由AcpiCpuStatus结构体的布尔字段支撑见 include/hw/acpi/cpu.h读取时打包返回见 hw/acpi/cpu.cval | cdev-cpu ? 1 : 0; /* bit0: enabled */ val | cdev-is_inserting ? 2 : 0; /* bit1: insert event */ val | cdev-is_removing ? 4 : 0; /* bit2: remove event */ val | cdev-fw_remove ? 16 : 0; /* bit4: fw eject requested */3.3 offset [0x5-0x7]保留读取返回 0。3.4 offset [0x8]Command dataDWORD 访问读取结果同样取决于Command field最后存储的值Command field 最后存储值读取结果0包含有待处理事件的 CPU 的CPU selector值3架构特定 CPU ID 值的低 32 位x86 场景下即APIC ID其它返回 0对应源码 hw/acpi/cpu.c命令CPHP_GET_NEXT_CPU_WITH_EVENT_CMD值 0时返回cpu_st-selector命令CPHP_GET_CPU_ID_CMD值 3时返回cdev-arch_id 0xFFFFFFFF。四、写访问行为Write Access Behavior4.1 offset [0x0-0x3]CPU selectorDWORD 访问选中当前活动的 CPU 设备。后续对其它寄存器的所有访问都将读写该 CPU 的数据。有效取值范围[0 .. max_cpus)对应源码 hw/acpi/cpu.ccpu_st-selector data。4.2 offset [0x4]CPU device control fields1 字节访问位语义bit 0保留OSPM 写入前必须将其清除bit 1若置 1清除设备插入事件。OSPM 在已对选中的 CPU 设备发出 device check 事件后置位bit 2若置 1清除设备移除事件。OSPM 在已对选中的 CPU 设备发出 device eject 请求后置位bit 3若置 1发起设备弹出。OSPM 触发 CPU 设备移除并调用_EJ0方法时置位或固件在 bit 4 置位时置位。若 bit 4 被置位本位将在弹出过程中被清除bit 4若置 1OSPM 将设备弹出交给固件执行。固件应按 bit 3 所述发出 eject 请求OSPM 在请求固件执行弹出时不应触碰 bit 3bit 5-7保留OSPM 写入前必须清除注意 bit 3发起弹出与 bit 4固件执行弹出是互斥的两条路径前者由 OSPM 自己执行_EJ0后者由 OSPM 委托固件。对应源码 hw/acpi/cpu.cif (data 2) { /* bit1: clear insert event */ cdev-is_inserting false; } else if (data 4) { /* bit2: clear remove event */ cdev-is_removing false; } else if (data 8) { /* bit3: initiate eject */ hotplug_ctrl qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, NULL); object_unparent(OBJECT(dev)); cdev-fw_remove false; } else if (data 16) { /* bit4: fw does eject */ cdev-fw_remove true; }从源码结构可以推断QEMU 不允许弹出首个 CPUfirst_cpu当选中无效设备或弹出首 CPU 时会拒绝并记录 trace见 hw/acpi/cpu.c。4.3 offset [0x5]Command field1 字节访问命令字段决定后续Command data寄存器的读写语义值语义0选择有插入/移除事件的 CPU 设备。随后读取Command data寄存器返回被选中的 CPUCPU selector值。若没有带事件的 CPU当前CPU selector不变且不修改对应的 insert/remove 事件标志1随后写入Command data寄存器将设置 QEMU 中的OST event寄存器2随后写入Command data寄存器将设置 QEMU 中的OST status寄存器3随后读取Command data与Command data 2将返回当前选中 CPU 的架构特定 CPU ID其它保留源码中的命令枚举定义见 hw/acpi/cpu.cenum { CPHP_GET_NEXT_CPU_WITH_EVENT_CMD 0, CPHP_OST_EVENT_CMD 1, CPHP_OST_STATUS_CMD 2, CPHP_GET_CPU_ID_CMD 3, CPHP_CMD_MAX };命令0的实现采用环形扫描从当前 selector 开始遍历所有设备找到第一个带事件is_inserting/is_removing/fw_remove的 CPU 并将其设为选中扫描一圈回到起点则保持原状见 hw/acpi/cpu.c。4.4 offset [0x6-0x7]保留写入被忽略。4.5 offset [0x8]Command dataDWORD 访问写入行为取决于Command field最后存储的值Command field 值写入行为1将值存入OST event 寄存器2将值存入OST status 寄存器并触发 QEMU 向外部应用发送ACPI_DEVICE_OSTQMP 事件事件携带当前 OST event 与 status 寄存器的值其它保留对应源码 hw/acpi/cpu.cOST status 写入后会构建ACPIOSTInfo并通过qapi_event_send_acpi_device_ost()发出 QMP 事件。而acpi_cpu_ospm_status()hw/acpi/cpu.c可遍历全部设备将每个 CPU 的 OST 状态导出为ACPIOSTInfoList供query-acpi-ospm-status类 QMP 查询使用。五、典型使用流程Typical Usecases规范给出了三条 OSPM 固件必须实现的标准操作路径以下为精确步骤复现。5.1 x86检测现代 CPU 热插拔接口QEMU 默认启用现代 CPU 热插拔接口固件按以下步骤探测其存在性向CPU selector寄存器写入0x0确保 selector 处于合法值向Command field寄存器写入0x0读取Command data 2寄存器读值为0x0→ 现代接口已启用否则 → 无可用 CPU 热插拔接口。该探测原理对应命令0下Command data 2恒返回 0 的读取语义见 hw/acpi/cpu.c。5.2 获取带待处理事件的 CPU向CPU selector写入0x0向Command field写入0x0读取CPU device status fields寄存器若读值中 bit 1 与 bit 2均为 0说明没有带待处理事件的 CPU当前 selector 保持不变否则读取Command data寄存器读值即为带事件 CPU 的 selector该 CPU 已被选中。5.3 枚举 present / non-present CPU将 present CPU 计数置 0将迭代器置 0向CPU selector写入0x0确保其处于合法状态且后续寄存器访问不会被忽略向Command field写入0x0使Command data返回当前选中 CPU 的 selector 值读取CPU device status fields寄存器若 bit 0 置位present CPU 计数加 1迭代器加 1将迭代器写入CPU selector寄存器读取Command data寄存器若读值非零跳回第 5 步否则向CPU selector写入0x0使其回到合法状态并退出——此时迭代器恰等于max_cpus。该流程利用了命令0下Command data返回当前 selector 的特性来探测该 selector 是否越界是固件遍历全部 possible CPU 的经典手法。六、源码级实现剖析6.1 状态结构与寄存器映射QEMU 用CPUHotplugState保存寄存器块的运行时状态include/hw/acpi/cpu.htypedef struct CPUHotplugState { MemoryRegion ctrl_reg; uint32_t selector; /* CPU selector 寄存器 */ uint8_t command; /* Command field 寄存器 */ uint32_t dev_count; /* possible CPU 总数 */ AcpiCpuStatus *devs; /* 每个 CPU 槽位的状态数组 */ } CPUHotplugState;每个 CPU 槽位的状态由AcpiCpuStatus描述include/hw/acpi/cpu.h包含cpu当前已插入的 CPUState 指针、arch_id架构特定 CPU IDx86 下即 APIC ID、is_inserting、is_removing、fw_remove以及ost_event/ost_status。寄存器偏移的宏定义hw/acpi/cpu.c与文档一一对应#define ACPI_CPU_SELECTOR_OFFSET_WR 0 /* CPU selector (0x0-0x3, 写) */ #define ACPI_CPU_FLAGS_OFFSET_RW 4 /* status/control (0x4, 读/写) */ #define ACPI_CPU_CMD_OFFSET_WR 5 /* Command field (0x5, 写) */ #define ACPI_CPU_CMD_DATA_OFFSET_RW 8 /* Command data (0x8, 读/写) */ #define ACPI_CPU_CMD_DATA2_OFFSET_R 0 /* Command data 2 (0x0-0x3, 读) */MemoryRegion 的访问属性hw/acpi/cpu.c允许 1~4 字节访问、小端序与文档DWORD 访问 / 1 字节访问的粒度约定吻合。6.2 插入与移除的事件流QEMU 设备模型侧通过三个回调驱动事件状态hw/acpi/cpu.cacpi_cpu_plug_cb()CPU 插入时若为运行期热插入设备dev-hotplugged置is_inserting true并通过acpi_send_event(..., ACPI_CPU_HOTPLUG_STATUS)拉高 GPE触发 BIOS 扫描acpi_cpu_unplug_request_cb()CPU 移除请求时置is_removing true并同样触发 GPEacpi_cpu_unplug_cb()移除完成后将cdev-cpu NULL槽位回归未启用状态。固件侧的事件发现则依赖命令0环形扫描与状态位读取二者配合完成QEMU 置位事件 → BIOS 扫描发现 → 通知 OSPM → OSPM 清除事件的完整闭环。6.3 迁移状态寄存器块状态通过vmstate_cpu_hotplug纳入迁移hw/acpi/cpu.c持久化selector、command以及每个设备的is_inserting、is_removing、ost_event、ost_status保证 live migration 后热插拔状态不丢失。相关宏VMSTATE_CPU_HOTPLUG定义于 include/hw/acpi/cpu.h。七、AML 固件侧寄存器如何变成 ACPI 对象寄存器块只是邮箱真正让 OSPM 可用的是build_cpus_aml()hw/acpi/cpu.c动态生成的 AML 代码它完成三件事声明资源设备\_SB.PRES_HID PNP0A06、_UID CPU Hotplug resources通过 OperationRegionPRST将 12 字节寄存器块映射为可访问的 ACPI 字段CPEN/CINS/CRMV/CEJ0/CEJF/CCMD/CSEL/CDAT并带一个互斥锁CPLK见 hw/acpi/cpu.c声明 CPU 容器设备\_SB.CPUS_HID ACPI0010、_CID PNP0A05实现CSCN扫描并通知、CTFY按 UID 通知对应 CPU、COST写 OST 事件/状态等控制方法以及每个 CPU 子设备的_STA、_MAT、_EJ0、_OST见 hw/acpi/cpu.c将扫描入口接到 GPE生成的event_handler_methodGPE.2 对应的处理函数只做一件事——调用\_SB.CPUS.CSCN见 hw/acpi/cpu.c。CSCN方法内部体现了两个值得注意的工程细节兼容 Windows XP为避免旧 Windows 崩溃新增/移除 CPU 列表使用 ACPI 1.0 的PackageOp最多 255 个元素并通过外层循环分批处理从而支持超过单批容量的 CPU 数量见 hw/acpi/cpu.c固件上电拉入fw unplug / SMI 回拨当固件协商了ICH9_LPC_SMI_F_CPU_HOTPLUG_BITopts.smi_path非空时扫描到新增 CPU 后会先通过 SMI 让固件把 CPU 拉入再向 OSPM 发送 notify见 hw/acpi/cpu.c_EJ0路径也会在opts.fw_unplugs_cpu时改为写CEJF并触发 SMI见 hw/acpi/cpu.c。八、测试与验证仓库通过 ACPI 表对比测试持续验证该接口在两种 x86 机型上的 AML 生成结果见 tests/qtest/bios-tables-test.cstatic void test_acpi_piix4_tcg_cphp(void) { data.machine MACHINE_PC; /* PIIX4: 0xaf00 */ test_acpi_one(-smp 2,cores3,sockets2,maxcpus6 -object memory-backend-ram,idram0,size64M -object memory-backend-ram,idram1,size64M -numa node,memdevram0 -numa node,memdevram1 -numa dist,src0,dst1,val21, data); }用例acpi/piix4/cpuhp与acpi/q35/cpuhp分别针对 PIIX40xaf00与 ICH90x0cd8两条基地址路径以-smp ... maxcpus6构造多 CPU 拓扑将生成的 DSDT 与基线.cphp变体比对任何寄存器布局、字段偏移或 AML 结构变化都会导致测试失败——这正是本规范最直接的回归守护。九、总结QEMU 现代 ACPI CPU 热插拔接口是一套设计紧凑的 12 字节 IO 寄存器协议selector 决定操作对象command 决定数据语义状态位承载插入/移除事件OST 通道实现操作系统对热插拔结果的上报。固件侧通过 GPE.2 CSCN扫描驱动整个事件循环而 QEMU 侧则以 hw/acpi/cpu.c 中的AcpiCpuStatus数组为状态中枢配合 tests/qtest/bios-tables-test.c 的 ACPI 表回归测试保证协议稳定性。对虚拟化平台开发者与固件工程师而言本文的寄存器语义表、典型操作序列与源码映射关系可直接作为实现 ACPI BIOS CPU 热插拔逻辑的参考基线。【免费下载链接】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),仅供参考
返回列表