Qemu行为级emulator而非simulator,在模拟目标架构时,会利用HOST架构的支持特性实现目标架构的行为,下面分析一下QEMU对目标架构原子指令的模拟方式。
HOST架构自然是X86,为了搭建环境的方便,选择RISCV架构作为目标架构,参考这篇文章搭建RISCV QEMU运行环境:
验证方案
1.首先,确保初始目标架构程序没有调用任何原子指令,验证方法是在QEMU中模拟TARGE架构流程的必经之路do_atomic_op_i32/do_atomic_op_i64打断点,确保初始程序没有调用任何目标架构上的原子操作指令。以本测试为例,riscv_programming_practice/chapter_2/benos下的初始测试程序就没有调用任何原子指令,先在do_atomic_op_i32/do_atomic_op_i64打上断点,之后运行程序,不会进入断点。
2.之后加入断点程序:
#include "uart.h"
static void arch_atomic_add(int i, unsigned long *v)
{
__asm__ __volatile__ ( "amoadd.d zero, %1, %0"
: "+A" (*v) :
"r" (i)
¦: "memory");
}
unsigned long abc;
void kernel_main(void)
{
arch_atomic_add(1, &abc);
uart_init();
uart_send_string("Welcome RISC-V!\r\n");
while (1) {
;
}
}
再次打上断点,发现这次拦截到了对原子操作指令的调用:
上述调用栈完成了对原子指令的翻译之后,之后开始执行GEN BUFFER中的指令,ATOMIC ADD指令具体由helper_atomic_fetch_addq_le函数实现:
helper_atomic_fetch_addq_le函数最终调用了X86 HOST主机的 "lock xadd"指令完成了对RISCV AMOADD指令的模拟操作。
所以,这一种模拟方式是使用HOST架构CPU上的原子指令来模拟目标主机架构上的原子指令。
RISCV amoswap.d指令的模拟
测试代码:
#include "uart.h"
unsigned long xchg(volatile void *ptr, unsigned long new)
{
unsigned long result;
asm volatile (
" amoswap.d %[result], %[new], (%[ptr])\n"
: [result]"=r"(result), [ptr]"+r"(ptr)
: [new]"r"(new)
: "memory");
return result;
}
void kernel_main(void)
{
unsigned long abc = 0;
xchg(&abc, 2);
uart_init();
uart_send_string("Welcome RISC-V!\r\n");
while (1) {
;
}
}
运行时抓到的调用堆栈如下,可以看到,最终是通过helper_atomic_xchgq_le helper函数实现的,顾名思义,那一定是调用了X86 HOST主机上的xchgq指令了:
不知为何,这里没有在HOST指令xchg前加LOCK,可能是个问题吧,不管怎样,TARGET机到HOST机上的原子指令映射关系找到了。