【airbus-seclab/qemu_blog加工翻译 02】深入了解 QEMU:一台新机器

在这篇文章中,我们将了解如何创建一台新机器。有机会介绍所谓的 QOM

可用机器和 CPU

可以使用 -M ? 列出给定架构的可用机器。命令行选项。

对于 PowerPC 目标架构,我们有:
在这里插入图片描述
一旦您选择了您最喜欢的机器,您还可以为相关机器选择可用的 CPU
在这里插入图片描述

CPIOM 机器

让我们创建一台新机器来支持 QEMU 中的 CPIOM。

在集成模块化航空电子设备 (IMA) 中,核心处理输入输出模块 (CPIOM) 是基于 PowerPC MPC-755 CPU 的嵌入式设备,具有 RAM、闪存、NVM、定时器、中断控制器、DMA 引擎、PCI 控制器和其他我们不会介绍的组件覆盖(AFDX、DSP)。
在这里插入图片描述

代码组织

首先,让我们通过将 cpiom 代码隔离在专用目录中来准备我们的环境。通常,添加到 QEMU 的每个对象都应放置在正确的目录中。它们是按性质组织的,而不是按目标用途或关联组织的。

  • 一台新的基于 PowerPC 的机器应该登陆 hw/ppc/
  • 新的串行设备hw/char/
  • 新的 PCI 主机控制器hw/pci-host/
  • 新的网络控制器hw/net/
  • ……
    在这里插入图片描述
    因此,让我们做相反的事情,将所有内容放在 hw/cpiom 中的同一位置。我们要做的第一件事是告诉 QEMU 如何构建我们的新机器和未来设备:
$ cat hw/cpiom/Makefile.objs
obj-y += board.o dev1.o dev2.o
$ cat hw/Makefile.objs
...
devices-dirs-y += cpiom/

创建新机器:hw/cpiom/board.c

要在 QEMU 中实现新机器,我们需要创建 QOM TypeInfo 及其关联的 MachineClassMachineState 初始化函数。

您可以找到有关 QOM 约定的文档

TypeInfo

我们可以使用预定义的宏:

DEFINE_MACHINE("cpiom", cpiom_machine_init)

生成以下代码:

static void cpiom_machine_init_class_init(ObjectClass *oc, void *data)
{
	MachineClass *mc = MACHINE_CLASS(oc);
	cpiom_machine_init(mc);
}
static const TypeInfo cpiom_machine_init_typeinfo = {
	.name = MACHINE_TYPE_NAME("cpiom"),
	.parent = TYPE_MACHINE,
	.class_init = cpiom_machine_init_class_init,
};
static void cpiom_machine_init_register_types(void)
{
	type_register_static(&cpiom_machine_init_typeinfo);
}
type_init(cpiom_machine_init_register_types)

MachineClass

正如 API 所说,我们的 class_init 函数将在调用所有父类 init 方法后调用。该类在该类的任何对象实例之前初始化。
我们来写一下:

void cpiom_machine_init(MachineClass *mc)
{
	mc->desc = "CPIOM board";
	mc->init = cpiom_init;
	mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("755_v2.8");
	mc->default_ram_size = CPIOM_MMAP_SDRAM_SIZE;
}

此时我们可以定义机器的具体属性,例如CPU型号、RAM大小,以及最重要的实例初始化方法cpiom_init

The MachineState

一旦类准备好,就会创建一个对象实例。我们之前提供了一个 mc->init = cpiom_init 函数

static void cpiom_init(MachineState *mcs)
{
   cpiom_t *cpiom = g_new0(cpiom_t, 1);
   cpiom_init_cpu(cpiom, mcs);
   cpiom_init_dev(cpiom);
   cpiom_init_boot(cpiom, mcs);
}

我们以后开发板功能实现的核心功能就在这里

为CPIOM初始化CPU : PowerPC 755

cpu 实例化将如下所示:

static void cpiom_init_cpu(cpiom_t *cpiom, MachineState *mcs)
{
	cpiom->cpu = POWERPC_CPU(cpu_create(mcs->cpu_type));
	/* Set time-base frequency to 16.6 Mhz */
	cpu_ppc_tb_init(&cpiom->cpu->env, CPIOM_TBFREQ);
	cpiom->cpu->env.spr[SPR_HID2] = 0x00040000;
	CPUClass *cc = CPU_GET_CLASS( CPU(cpiom->cpu) );
	PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(cc);
	CPUPPCState *env = &cpiom->cpu->env;
	/* Default mmu_model for MPC755 is POWERPC_MMU_SOFT_6xx (no
	softmmu) */
	pcc->mmu_model = POWERPC_MMU_32B;
	env->mmu_model = POWERPC_MMU_32B;
	pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
}

获取PowerPC CPU状态对象

通用 cpu_create() 函数将创建正确类型的 CPU 对象。我们想要实例化一个 PowerPC CPU,其特定类型已在类 init 期间定义((POWERPC_CPU_TYPE_NAME("755_v2.8"))
该代码相当于:

CPUState *cpu = CPU(object_new( POWERPC_CPU_TYPE_NAME("755_v2.8")
));

QOM 类型实例化很复杂并且没有那么有趣。我们应该记住,我们需要 755 系列的 PowerPC cpu。请求的 CPU 型号和系列在 target/ppc/cpu-models.ctarget/ppc/translate_init.inc.c 中定义:

POWERPC_DEF("755_v2.8", CPU_POWERPC_7x5_v28, 755, "PowerPC 755 v2.8")
POWERPC_FAMILY(755)(ObjectClass *oc, void *data)
{
	DeviceClass *dc = DEVICE_CLASS(oc);
	PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
	dc->desc = "PowerPC 755";
	pcc->init_proc = init_proc_755;
	...
	pcc->mmu_model = POWERPC_MMU_SOFT_6xx;
	pcc->excp_model = POWERPC_EXCP_7x5;
	pcc->bus_model = PPC_FLAGS_INPUT_6xx;
	pcc->bfd_mach = bfd_mach_ppc_750;
	pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE |
	POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK;
}
static void init_proc_755(CPUPPCState *env)
{
	...
	init_excp_7x5(env);
	ppc6xx_irq_init(ppc_env_get_cpu(env));
	....
}

这是非常低级且详细的 CPU 初始化代码。我们可以看到MMU类型、中断和异常初始化代码。

访问您的 PowerPC CPU

在 QEMU 代码中,您通常会找到动态类型转换宏,例如 DEVICE_CLASS、CPU_CLASS、CPU 或 POWERPC_CPU 。它们通过设备所属的继承类进行安全类型检查。它们帮助您检索给定运行时对象的正确定义。

#define POWERPC_CPU(obj) OBJECT_CHECK(PowerPCCPU, (obj), TYPE_POWERPC_CPU)

以下是一些用例:

CPUState 		*cs = CPU(cpiom->cpu); 
CPUClass		*cc = CPU_GET_CLASS(cs); 
PowerPCCPU 		*ppc = POWERPC_CPU(cs); 
PowerPCCPUClass *ppc_cc = POWERPC_CPU_CLASS(cc); 
CPUPPCState 	*ppc_cs = &ppc->env;

CPUClass 中,您将找到与 CPU 系列相关的函数指针,例如执行、中断、mmu 故障处理。虽然 CPUState 将保存 CPU 的通用状态,例如其运行状态、配置的断点、最后引发的异常……
它们都与整体 QEMU CPU 设计相关。您可能感兴趣的部分是跟踪 CPU 寄存器的环境指针。在我们的例子中,查找 CPUPPCState

struct PowerPCCPU {
...
CPUPPCState env;
...
}
struct CPUPPCState {
	...
	/* general purpose registers */
	target_ulong gpr[32];
	/* Storage for GPR MSB, used by the SPE extension */
	target_ulong gprh[32];
	...
}

因此,如果您想访问 CPIOM CPU 的 GPR[14],您将最终执行以下操作:

cpiom->cpu->env.gpr[14] = xxxx;

为 CPIOM 强制使用软件

MMU 创建 CPU 对象后,我们可以继续进行一些调整,例如对其核心频率或特定于模型的寄存器默认值进行调整。我们的 init 函数还负责启用对 MPC755 系列的软件 MMU 支持,但默认情况下并非如此:

static void cpiom_init_cpu(cpiom_t *cpiom, MachineState *mcs)
{	
	...
	CPUClass *cc = CPU_GET_CLASS( CPU(cpiom->cpu) );
	PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(cc);
	CPUPPCState *env = &cpiom->cpu->env;
	/* Default mmu_model for MPC755 is POWERPC_MMU_SOFT_6xx (no
	softmmu) */
	pcc->mmu_model = POWERPC_MMU_32B;
	env->mmu_model = POWERPC_MMU_32B;
	pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault;
}

在 QEMU 术语中,softmmu 代表软件内存管理单元。这是CPU硬件MMU的软件实现。 QEMU 源代码中提供了针对不同架构的多种实现。
在专门讨论 QEMU 内存内部结构的文章中,我们将解释地址转换是如何操作的以及为什么我们必须这样做。

结论

这并不详尽,但您明白了。如果您需要对 CPU 初始化进行更多控制,请查看其定义。

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值