【airbus-seclab/qemu_blog加工翻译 03】深入探讨 QEMU:内存区域

在这篇文章中,我们将了解 QEMU 中的高级内存组织:内存区域 (MR)。

我们不会讨论地址空间,因为我们通常直接管理内存区域。但是,请查看 docs/devel/memory 和代码(include/exec/memory.h)以获取更多详细信息。
那里提供了内存组织的高级外部演示。您还可以在 docs/devel/loads-stores 找到非常有趣的内部文档。这是用于访问内存的可用 QEMU API 的枚举。

当您想使用 QEMU 中的内存区域时,您可以:

  • 获取指向支持 VM 内存区域的主机缓冲区的直接指针
  • 实现读/写回调函数以拦截每次访问(通常是 IO 内存)
  • 使用 QEMU cpu_physical_memory_rw() 安全地使用访问区域

在专门讨论 TCG 的博客文章中,我们将准确了解翻译后的指令如何访问 VM 内存以及我们如何在此级别进行拦截。

查看内存树(略)

下面是PowerPC 40p板准备就绪后的可用内存区域树。如您所见,内存区域可以包含其他内存区域(称为子区域)。这是一种干净的组织内存的方式。每个内存区域都有自己的属性,并附加到一种称为地址空间的视图。

qemu-system-ppc -M 40p -s -S -monitor stdio

qemu-system-ppc: 这是运行PowerPC架构虚拟机的QEMU命令。
-M 40p: 指定虚拟机所使用的机器型号,这里是40p,代表Power Mac G4。这是指定虚拟机硬件的参数。
-s: 启用GDB服务器,允许通过GDB(GNU Debugger)连接到QEMU实例,以便进行调试。
-S: 在启动时冻结虚拟机,等待GDB连接后才继续执行。这样可以在虚拟机启动时暂停,以便进行调试配置和准备。
-monitor stdio: 打开监视器,并将其连接到标准输入输出(stdio)。这允许用户在终端中通过监视器与虚拟机进行交互,例如输入命令来监视和控制虚拟机的状态。
综合起来,这个命令启动了一个PowerPC架构的虚拟机(Power Mac G4),并启用了GDB服务器,使得用户可以通过GDB连接到虚拟机进行调试。虚拟机在启动时会被冻结,等待GDB连接,同时用户可以通过监视器与虚拟机进行交互。

在这里插入图片描述

默认内存区域和地址空间由 QEMU 创建。最重要的是由cpu_exec_init_all() 中的 memory_map_init() 创建的系统内存区域

它可以看作是顶层的,通常会在系统内存区域中添加子区域

分配系统内存

这可能是创建新机器时最想要的事情之一:获取 RAM 并加载固件。要调用的正确函数是 memory_region_allocate_system_memory()

如果我们看看其他一些主板实现,例如 MIPS R4K:

void mips_r4k_init(MachineState *machine)
{
	MemoryRegion *sys_mem = get_system_memory();
	MemoryRegion *ram = g_new(MemoryRegion, 1);
	memory_region_allocate_system_memory(ram, NULL, "mips_r4k.ram",
	ram_size);
	memory_region_add_subregion(sys_mem, 0, ram);
	...
}

为 RAM 创建一个新的内存区域,并将其直接添加为系统内存区域的子区域。从那时起,访问物理地址 0 ram_size 将访问 VM 内存。
memory_region_allocate_system_memory 函数实际上从 QEMU 内存 API 调用 memory_region_init_ram_shared_nomigrate()。它封装了 RAM 特定内存区域的初始化。

QEMU 内存 API 允许您创建由文件描述符、已分配的主机缓冲区和回调支持的内存区域,正如我们将在 IO 中看到的那样。

IO 内存区域

回到我们简单的 MIPS 板示例:

void mips_r4k_init(MachineState *machine)
{
	...
	MemoryRegion *iomem = g_new(MemoryRegion, 1);
	memory_region_init_io(iomem, NULL, &mips_qemu_ops, NULL, "mipsqemu", 0x10000);
	memory_region_add_subregion(sys_mem, 0x1fbf0000, iomem);
}

使用 memory_region_init_io() 创建新的内存区域iomem ,并将其添加为系统内存的子区域。该区域不是 RAM 而是 IO 类型,并且有一个特殊的 MemoryRegionOps 参数

static const MemoryRegionOps mips_qemu_ops = {
	.read = mips_qemu_read,
	.write = mips_qemu_write,
	.endianness = DEVICE_NATIVE_ENDIAN,
};
static void mips_qemu_write (void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
	if ((addr & 0xffff) == 0 && val == 42)
	qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
	...
	}
static uint64_t mips_qemu_read (void *opaque, hwaddr addr, unsigned size)
{
	return 0;
}

IO 内存区域暴露设备内存。它们通常需要在读/写访问期间进行特殊解释以模拟预期的设备行为。使用MemoryRegionOps回调可以帮助您实现设备操作。

在前面的示例中,iomem 区域映射到 0x1fbf0000 0x1fc00000。每当虚拟机访问此内存范围时,就会调用读/写回调。 addr 参数是距相关内存区域开头的偏移量。

因此,在此 MIPS 板上执行类似 *(u8*)0x1fbf0000 = 42 的操作会触发系统重置。

关于 CPIOM SDRAM 怎么样

有时您可能希望将两者结合起来用于特定设备。设备内存的一部分没有特殊含义,用于事务处理,而一小部分可能被解释为设备寄存器。

static void cpiom_sdram_init(Object *obj)
{
    cpiom_sdram_state_t *s = CPIOM_SDRAM(obj);
    SysBusDevice        *d = SYS_BUS_DEVICE(obj);
    MemoryRegion        *sysmem = get_system_memory();

    memory_region_allocate_system_memory(&s->ram, obj,
                                         CPIOM_SDRAM_NAME,
                                         CPIOM_MMAP_SDRAM_SIZE);
    memory_region_init_io(&s->reg, obj, &sdram_reg_ops, s,
                          CPIOM_SDRAM_NAME"-reg",
                          CPIOM_MMAP_SDRAM_REG_SIZE);
    memory_region_init_io(&s->pcode.mmio, obj, &sdram_pcode_ops, s,
                          CPIOM_SDRAM_NAME"-pcode",
                          CPIOM_MMAP_SDRAM_PCODE_SIZE);
    memory_region_init_io(&s->pdata.mmio, obj, &sdram_pdata_ops, s,
                          CPIOM_SDRAM_NAME"-pdata",
                          CPIOM_MMAP_SDRAM_PDATA_SIZE);
    memory_region_init_io(&s->raf, obj, &sdram_raf_ops, s,
                          CPIOM_SDRAM_NAME"-raf", 1*4);

    memory_region_add_subregion(sysmem, 0, &s->ram);
    sysbus_init_mmio(d, &s->reg);
    memory_region_add_subregion(sysmem,
                                CPIOM_MMAP_SDRAM_PCODE, &s->pcode.mmio);
    memory_region_add_subregion(sysmem,
                                CPIOM_MMAP_SDRAM_PDATA, &s->pdata.mmio);

    sysbus_init_irq(d, &s->irq_viol);
    sysbus_init_irq(d, &s->irq_err);
}

在这个更复杂的示例中,CPIOM SDRAM 是一个成熟的 QEMU 设备对象,具有多个内存区域。某些内存区域(reg 和 raf)不会直接作为子区域添加到系统内存中。我们将在下一篇专门介绍设备创建的文章中解释原因。

CPIOM SDRAM 控制器提供代码和数据保护以及刷新率调节。它还能够提出中断请求。

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值