QEMU-KVM:来宾物理地址GBA是否与主机上 QEMU-KVM 的虚拟地址相同?

问题描述

例如,如果guest上的进程在0x8000上分配了数据(如在Guest Virtual Memory中),并且已映射到Guest Physical Memory Address Space中的0x4000,则该数据位于虚拟内存地址中的0x4000主机端 QEMU-KVM 的(子)进程(每个 VM)的空间?换句话说,如果我在 QEMU 的源代码中编写新代码(这样我可以使用 QEMU-KVM 的页表),编译并运行它,那么我可以直接访问来宾进程的数据,只要来宾的物理内存地址匹配到来宾虚拟内存地址?

解决方法

不,用于用户空间 QEMU 进程的页表映射与来宾本身用于来宾虚拟 -> 来宾物理地址映射的页表或 KVM 内核代码使用的页表无关来宾物理地址 -> RAM 的主机物理地址映射。

当您在 QEMU 中编写需要访问客户内存的代码时,您应该使用 QEMU 提供的 API 来实现,这些 API 处理将客户地址转换为 RAM 的主机虚拟地址以及处理当该访客地址具有模拟设备而不是 RAM 时。 QEMU 开发人员内部文档有 a section on APIs for loads and stores,这些函数也记录在头文件的文档注释中。

通常最好的建议是“在 QEMU 中找到一些现有的代码,这些代码的作用与您正在尝试做的事情基本相同,并以此为例”。

,

我假设您的问题是关于 x86 平台的。 QEMU+KVM 使用扩展页表 (EPT) 将主机虚拟地址 (HVA) 映射到客户物理地址 (GPA)。要找到GPA对应的HVA,你应该遍历你guest的EPT。

当您需要从客户机读取虚拟地址时,您可以使用 QEMU 源代码中的 GDB 函数。此代码段将在您的来宾当前执行过程中的 virtualAddress 处读取 4 个字节:

  uint8_t instr[4];
  if( cpu_memory_rw_debug( cpu,virtualAddress,(uint8_t *)instr,sizeof( instr ),0 ) )
  {
    printf( "Failed to read instr!\n" );
    return;
  }

cpu 是您的访客 CPU 的 CPUState*

当你想在你的guest中读取一个具体进程的地址时,你应该将这个进程的env->cr[3]设置为CR3值(env是一个CPUX86State*) .阅读完后不要忘记恢复原始值。当然,只有在您的来宾未执行时才读取内存,否则可能会出现竞争。