QEMU 与 ACPI BIOS CPU 热插拔接口
QEMU 通过 ACPI 支持 CPU 热插拔。本文描述 QEMU 与 ACPI BIOS 的接口。
ACPI BIOS 的 GPE.2 处理程序专门向操作系统通知 CPU 热添加和热移除事件。
现代 ACPI CPU 热插拔接口寄存器
寄存器块基址:
ICH9-LPC I/O 端口 0x0cd8
PIIX-PM I/O 端口 0xaf00
寄存器块大小:
ACPI_CPU_HOTPLUG_REG_LEN = 12
以下所有寄存器访问均采用小端字节序。
保留寄存器的行为:
忽略写入。
读取返回全 0。
CPU selector 最后写入的值必须指向可能存在的 CPU,否则:
读取任意寄存器都返回 0。
忽略其他寄存器写入,直到选择器重新写入有效值。
QEMU 启动时将 CPU selector 初始化为有效值,复位时保留当前值。
读取行为
- 偏移 [0x0-0x3]
Command data 2,DWORD 访问。
根据 Command field 最后写入值:
- 0:
读取为 0x0。
- 3:
架构专用 CPU ID 的高 32 位。
- 其他值:
保留。
- 偏移 [0x4]
CPU 设备状态字段,1 字节访问。
位定义:
- 0:
设备已启用,客户机可使用。
- 1:
设备插入事件,识别尚未向 OSPM 发送设备检查事件的设备。 仅在位 0 已设置时有效。
- 2:
设备移除事件,识别尚未向 OSPM 发送弹出请求的设备。固件必须忽略此位。
- 3:
保留,OSPM 应忽略。
- 4:
为 1 时,OSPM 请求固件执行设备弹出。
- 5-7:
保留,OSPM 应忽略。
- 偏移 [0x5-0x7]
保留。
- 偏移 [0x8]
Command data,DWORD 访问。
根据 Command field 最后写入值:
- 0:
包含有待处理事件的 CPU 的选择器值。
- 3:
架构专用 CPU ID 的低 32 位,x86 为 APIC ID。
- 其他值:
包含 0。
写入行为
- 偏移 [0x0-0x3]
CPU selector,DWORD 访问。
选择活动 CPU,此后其他寄存器访问均读写所选 CPU 数据。 有效范围为 [0 .. max_cpus)。
- 偏移 [0x4]
CPU 设备控制字段,1 字节访问。
位定义:
- 0:
保留,OSPM 写入前必须清零。
- 1:
写 1 清除插入事件,OSPM 为所选 CPU 发出设备检查事件后设置。
- 2:
写 1 清除移除事件,OSPM 为所选 CPU 发出弹出请求后设置。
- 3:
写 1 发起弹出。OSPM 触发 CPU 移除并调用 _EJ0 时设置, 或在位 4 已设置时由固件设置。若位 4 已设置,弹出时将其清零。
- 4:
写 1 表示 OSPM 将弹出交给固件。固件应按上述位 3 发起弹出请求。 已要求固件执行时,OSPM 不应再操作位 3。
- 5-7:
保留,OSPM 写入前必须清零。
- 偏移 [0x5]
Command field,1 字节访问。
值定义:
- 0:
选择有插入/移除事件的 CPU,后续读取 Command data 返回所选 CPU 的选择器值。 若无待处理事件,当前选择器不变,也不修改相应插入/移除标志。
- 1:
后续写入 Command data 设置 QEMU 的 OST 事件寄存器。
- 2:
后续写入 Command data 设置 QEMU 的 OST 状态寄存器。
- 3:
后续读取 Command data 和 Command data 2 返回当前 CPU 的架构专用 ID。
- 其他值:
保留。
- 偏移 [0x6-0x7]
保留。
- 偏移 [0x8]
Command data,DWORD 访问。
根据 Command field 最后写入值:
- 1:
将值保存到 OST 事件寄存器。
- 2:
将值保存到 OST 状态寄存器,触发 QEMU 向外部应用发送 ACPI_DEVICE_OST QMP 事件, 包含当前 OST 事件和状态寄存器值。
- 其他值:
保留。
典型用例
x86:检测并启用现代 CPU 热插拔接口
QEMU 启动时已启用现代接口,可按以下步骤检测:
向 CPU selector 写 0x0,确保选择器有效。
向 Command field 写 0x0。
读取 Command data 2;若值为 0x0,现代接口已启用,否则没有可用 CPU 热插拔接口。
获取有待处理事件的 CPU
向 CPU selector 写 0x0。
向 Command field 写 0x0。
读取 CPU 设备状态字段。
若位 1、2 均为 0,则没有待处理事件,所选 CPU 保持不变。
否则读取 Command data,其值为有待处理事件且已被选中的 CPU 选择器。
枚举存在与不存在的 CPU
将现有 CPU 计数设为 0。
将迭代器设为 0。
向 CPU selector 写 0x0,确保有效,避免其他寄存器访问被忽略。
向 Command field 写 0x0,使 Command data 返回所选 CPU 选择器值。
读取 CPU 设备状态字段。
位 0 为 1 时递增现有 CPU 计数。
递增迭代器。
将迭代器写入 CPU selector。
读取 Command data。
若值非 0,跳转到第 5 步。
否则向 CPU selector 写 0x0 恢复有效状态并退出。 此时迭代器等于 max_cpus。