加载和存储 API
QEMU 内部有多组加载和存储函数。 本文列出这些函数并说明适用场景,不详细描述各 API; 具体接口说明应查阅相应头文件的文档注释。
ld*_p and st*_p
这些函数操作主机指针,适用于已取得主机内存指针的情况, 该内存可能对应客户机 RAM 或本地缓冲区。 它们按所需字节序访问,并正确处理可能未对齐的指针。
函数命名模式如下:
加载: ld{sign}{size}_{endian}_p(ptr)
存储: st{size}_{endian}_p(ptr, val)
sign空:用于 32 或 64 位。
u:无符号。s:有符号。
sizeb:8 位。w:16 位。24:24 位。l:32 位。q:64 位。
endianhe:主机字节序。be:大端。le:小端。
按目标字节序访问时,省略 _{endian} 中缀。
目标字节序访问函数只供按目标分别编译的源码使用。
也有把大小作为参数的函数:
加载: ldn_{endian}_p(ptr, sz)
它从 ptr 按指定字节序无符号读取 sz 字节,以 uint64_t 返回。
存储: stn_{endian}_p(ptr, sz, val)
它将 val 按指定字节序以 sz 字节写入 ptr。
- 用于 git grep 的正则表达式:
\<ld[us]\?[bwlq]\(_[hbl]e\)\?_p\>\<st[bwlq]\(_[hbl]e\)\?_p\>\<st24\(_[hbl]e\)\?_p\>\<ldn\(_[hbl]e\)\?_p\>\<stn\(_[hbl]e\)\?_p\>
cpu_{ld,st}*_mmu
这些函数操作客户机虚拟地址,另接收控制地址转换的 MMU 索引上下文, 以及包含对齐要求等信息的 MemOp。 MemOp 与 MMU 索引合并为一个 MemOpIdx 参数。
索引含义取决于目标架构。 例如辅助函数要求始终以非特权方式访问,而非按当前客户机 CPU 状态默认访问时, 就可能需要指定特定索引。
这些函数可能触发客户机 CPU 异常,例如对齐或 MMU 错误, 更新客户机状态并通过 longjmp 跳出调用。 因此只能用于实现客户机 CPU 模拟的代码。
retaddr 传给 cpu_restore_state(),控制异常时客户机 CPU 状态的回溯恢复。 值应为 0,表示状态已同步,或为顶层 HELPER(foo) 中 GETPC() 的结果, 即指向生成代码的返回地址 [1]。
函数命名模式如下:
加载: cpu_ld{size}{end}_mmu(env, ptr, oi, retaddr)
存储: cpu_st{size}{end}_mmu(env, ptr, val, oi, retaddr)
sizeb:8 位。w:16 位。l:32 位。q:64 位。
end空:用于目标字节序或 8 位访问。
_be:大端。_le:小端。
- 用于 git grep 的正则表达式:
\<cpu_ld[bwlq]\(_[bl]e\)\?_mmu\>\<cpu_st[bwlq]\(_[bl]e\)\?_mmu\>
cpu_{ld,st}*_mmuidx_ra
类似 cpu_{ld,st}_mmu,但 mmuidx 不与 MemOp 合并, 因此不提供也不强制对齐要求。
函数命名模式如下:
加载: cpu_ld{sign}{size}{end}_mmuidx_ra(env, ptr, mmuidx, retaddr)
存储: cpu_st{size}{end}_mmuidx_ra(env, ptr, val, mmuidx, retaddr)
sign空:用于 32 或 64 位。
u:无符号。s:有符号。
sizeb:8 位。w:16 位。l:32 位。q:64 位。
end空:用于目标字节序或 8 位访问。
_be:大端。_le:小端。
- 用于 git grep 的正则表达式:
\<cpu_ld[us]\?[bwlq]\(_[bl]e\)\?_mmuidx_ra\>\<cpu_st[bwlq]\(_[bl]e\)\?_mmuidx_ra\>
cpu_{ld,st}*_data_ra
类似 cpu_{ld,st}_mmuidx_ra,但 mmuidx 来自客户机 CPU 当前模式, 由 cpu_mmu_index(env, false) 决定。
辅助函数按客户机虚拟地址访问时,通常首选这组函数, 除非需要非默认上下文或强制对齐。
函数命名模式如下:
加载: cpu_ld{sign}{size}{end}_data_ra(env, ptr, ra)
存储: cpu_st{size}{end}_data_ra(env, ptr, val, ra)
sign空:用于 32 或 64 位。
u:无符号。s:有符号。
sizeb:8 位。w:16 位。l:32 位。q:64 位。
end空:用于目标字节序或 8 位访问。
_be:大端。_le:小端。
- 用于 git grep 的正则表达式:
\<cpu_ld[us]\?[bwlq]\(_[bl]e\)\?_data_ra\>\<cpu_st[bwlq]\(_[bl]e\)\?_data_ra\>
cpu_{ld,st}*_data
类似 cpu_{ld,st}_data_ra,但 retaddr 为 0,因此不回溯恢复客户机 CPU 状态。
因此只适用于翻译器已保存全部必要 CPU 状态的辅助函数。 从 do_interrupt 等 CPU 钩子调用,或能确定翻译器必然已保存所有状态时,应使用它们。
函数命名模式如下:
加载: cpu_ld{sign}{size}{end}_data(env, ptr)
存储: cpu_st{size}{end}_data(env, ptr, val)
sign空:用于 32 或 64 位。
u:无符号。s:有符号。
sizeb:8 位。w:16 位。l:32 位。q:64 位。
end空:用于目标字节序或 8 位访问。
_be:大端。_le:小端。
- 用于 git grep 的正则表达式:
\<cpu_ld[us]\?[bwlq]\(_[bl]e\)\?_data\>\<cpu_st[bwlq]\(_[bl]e\)\?_data\+\>
cpu_ld*_code_mmu
类似 cpu_{ld,st}*_mmu,但执行用于指令执行的读取。 产生的客户机异常会标识为指令执行故障,而非数据读取故障。
翻译期间一般不应直接使用,应使用同时处理跟踪插件的封装函数。
函数命名模式如下:
加载: cpu_ld{sign}{size}_code_mmu(env, addr, oi, retaddr)
sign空:用于 32 或 64 位。
u:无符号。s:有符号。
sizeb:8 位。w:16 位。l:32 位。q:64 位。
- 用于 git grep 的正则表达式:
\<cpu_ld[us]\?[bwlq]_code_mmu\>
translator_ld*
封装 cpu_ld*_code_mmu,并执行跟踪插件所需操作。 只能在翻译器的 translate_insn 回调期间调用。
以 _swap 结尾的函数在参数为 true 时, 按 TARGET_BIG_ENDIAN 所确定的客户机原生字节序的相反顺序返回值。
函数命名模式如下:
加载: translator_ld{sign}{size}(env, ptr)
字节交换: translator_ld{sign}{size}_swap(env, ptr, swap)
sign空:用于 32 或 64 位。
u:无符号。s:有符号。
sizeb:8 位。w:16 位。l:32 位。q:64 位。
- 用于 git grep 的正则表达式:
\<translator_ld[us]\?[bwlq]\(_swap\)\?\>
helper_{ld,st}*_mmu
主要由 TCG 后端生成的代码调用。 类似 cpu_{ld,st}_mmu,使用指定 MemOpIdx 按客户机虚拟地址访问。
区别是字节序仅来自 MemOpIdx, 加载还会把返回值扩展到主机通用寄存器大小,即 tcg_target_ulong。
加载: helper_ld{sign}{size}_mmu(env, addr, opindex, retaddr)
存储: helper_{size}_mmu(env, addr, val, opindex, retaddr)
sign空:用于 32 或 64 位。
u:无符号。s:有符号。
sizeb:8 位。w:16 位。l:32 位。q:64 位。
- 用于 git grep 的正则表达式:
\<helper_ld[us]\?[bwlq]_mmu\>\<helper_st[bwlq]_mmu\>
address_space_*
这是模拟 CPU 或设备内存访问的主要函数。 接收 AddressSpace,表示设备或 CPU 看到的内存视图, 通常对应硬件总线或互连结构的主设备端。
每个 CPU 都有 AddressSpace,部分 CPU 有多个, 例如支持 TrustZone 的 Arm 分别为安全世界和非安全世界设置地址空间。 能执行 DMA 的设备通常也应有自己的 AddressSpace。 系统地址空间一般包含全部 CPU 可见的设备和内存。 部分旧设备模型直接使用它,而未正确建模自身地址空间。
提供按字节缓冲区读写及单个数据项加载/存储两类接口。
调用者均需提供 MemTxAttrs 指定总线事务属性, 并可通过 MemTxResult 返回码检查是否成功。
address_space_read(address_space, addr, attrs, buf, len)
address_space_write(address_space, addr, attrs, buf, len)
address_space_rw(address_space, addr, attrs, buf, len, is_write)
address_space_ld{sign}{size}_{endian}(address_space, addr, attrs, txresult)
address_space_st{size}_{endian}(address_space, addr, val, attrs, txresult)
sign空:用于 32 或 64 位。
u:无符号。
(不提供有符号加载。)
sizeb:8 位。w:16 位。l:32 位。q:64 位。
endianle:小端。be:大端。
字节访问省略 _{endian} 后缀。
- 用于 git grep 的正则表达式:
\<address_space_\(read\|write\|rw\)\>\<address_space_ldu\?[bwql]\(_[lb]e\)\?\>\<address_space_st[bwql]\(_[lb]e\)\?\>
address_space_write_rom
类似 address_space_write,按物理地址写入, 但对 ROM 也会修改内容,而客户机 CPU 的普通 ROM 写入会被忽略。 用于 GDB 调试桩写入或 ROM 初始加载等非客户机写操作。
写入范围中针对设备的部分会静默忽略,只写实际 RAM 和 ROM。
- 用于 git grep 的正则表达式:
address_space_write_rom
{ld,st}*_phys
类似 address_space_{ld,st}*,但事务属性始终为 MEMTXATTRS_UNSPECIFIED, 并忽略事务成败。
由于忽略结果,新代码不应使用, 除非能确定访问方 CPU 或设备本来就没有报告此类错误的机制。
load: ld{sign}{size}_{endian}_phys
store: st{size}_{endian}_phys
sign空:用于 32 或 64 位。
u:无符号。
(不提供有符号加载。)
sizeb:8 位。w:16 位。l:32 位。q:64 位。
endianle:小端。be:大端。
字节访问省略 _{endian}_ 中缀。
- 用于 git grep 的正则表达式:
\<ldu\?[bwlq]\(_[bl]e\)\?_phys\>\<st[bwlq]\(_[bl]e\)\?_phys\>
physical_memory_*
这些便捷函数类似 address_space_*,但固定操作旧式全局 &address_space_memory,并非所有机器都使用该空间。 属性始终为 MEMTXATTRS_UNSPECIFIED,且忽略事务成败。 预期供硬件设备模型使用,但新代码最好避免,原因是:
读写失败时,很可能需要正确模拟相应行为;
设备通常应在自己的 AddressSpace 操作,而非系统地址空间;
部分机器根本不使用这个全局地址空间。
physical_memory_read
physical_memory_write
- 用于 git grep 的正则表达式:
\<physical_memory_\(read\|write\)\>
cpu_memory_rw_debug
为调试目的按虚拟地址访问 CPU 内存。
供 GDB 桩及类似代码使用。根据指定 CPU 当前设置通过 MMU 查找, 将虚拟地址转为物理地址,再执行访问。 读取用 address_space_rw,写入用 address_space_write_rom。 因此写 ROM 时会实际修改,而普通客户机 CPU 写入会被忽略。
cpu_memory_rw_debug
dma_memory_*
类似 address_space_*,但先执行 DMA 屏障。
待办: 应说明何时需要 DMA 屏障,何时可直接用 address_space_*, 并确认现有代码处理正确。
dma_memory_read
dma_memory_write
dma_memory_rw
- 用于 git grep 的正则表达式:
\<dma_memory_\(read\|write\|rw\)\>\<ldu\?[bwlq]\(_[bl]e\)\?_dma\>\<st[bwlq]\(_[bl]e\)\?_dma\>
pci_dma_* 与 {ld,st}*_pci_dma
专供作为总线主设备发起访问的 PCI 设备模型使用。
传入 PCIDevice * 后,它们在该设备正确的地址空间执行 dma_memory_* 操作。
pci_dma_read
pci_dma_write
pci_dma_rw
load: ld{sign}{size}_{endian}_pci_dma
store: st{size}_{endian}_pci_dma
sign空:用于 32 或 64 位。
u:无符号。
(不提供有符号加载。)
sizeb:8 位。w:16 位。l:32 位。q:64 位。
endianle:小端。be:大端。
字节访问省略 _{endian}_ 中缀。
- 用于 git grep 的正则表达式:
\<pci_dma_\(read\|write\|rw\)\>\<ldu\?[bwlq]\(_[bl]e\)\?_pci_dma\>\<st[bwlq]\(_[bl]e\)\?_pci_dma\>