在这篇文章中,我们将了解如何创建一台新机器。有机会介绍所谓的 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 及其关联的 MachineClass 和 MachineState 初始化函数。
您可以找到有关 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.c
和 target/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 初始化进行更多控制,请查看其定义。