Gxemul支援ARM,MIPS,PPC内核的虚拟机,这里以oldtestmips机器类型为例,介绍gxemul如何运行
1 主流程,以虚拟机的生命周期简单说明
main.cc main()
{
cpu_init(); 初始化CPU模块,注册不同的cpu family函数
device_init(); 初始化device模块
machine_init(); 初始化machine,注册不同的machine setup函数
emul = emul_new(NULL); 创建虚拟机
get_cmd_args (argc, argv, emul, &diskimages, &n_diskimages) 解析参数
emul_simple_init(emul); 初始化虚拟机
emul_run(emul); 运行虚拟机,虚拟机运行后在这个函数内loop执行指令
emul_destroy(emul); 删除虚拟机
}
2 初始化CPU
cpu.cc cpu_init
{
ADD_ALL_CPU_FAMILIES;
}
cpu.cc add_cpu_family(mips_cpu_family_init, ARCH_MIPS);
{
res = family_init(fp);
}
对于mips来说family_init就是mips_cpu_family_init,由tmp_mips_tail.cc使用cpu.h的宏CPU_FAMILY_INIT生成,请在tmp_mips_tail.cc是在编译时由generate_tail.c产生
tmp_mips_tail.cc CPU_FAMILY_INIT(mips,"MIPS")
#define CPU_FAMILY_INIT(n,s) int n ## _cpu_family_init( \
struct cpu_family *fp) { \
/* Fill in the cpu_family struct with valid data for this arch. */ \
fp->name = strdup(s); \
fp->cpu_new = n ## _cpu_new; \
fp->list_available_types = n ## _cpu_list_available_types; \
fp->disassemble_instr = n ## _cpu_disassemble_instr; \
fp->register_dump = n ## _cpu_register_dump; \
fp->dumpinfo = n ## _cpu_dumpinfo; \
fp->functioncall_trace = n ## _cpu_functioncall_trace; \
fp->tlbdump = n ## _cpu_tlbdump; \
fp->init_tables = n ## _cpu_init_tables; \
return 1; \
}
展开为:
mips_cpu_family_init(struct cpu_family *)
{
fp->name = strdup(“MIPS”);
fp->cpu_new = mips _cpu_new;
…..
fp->init_tables = mips _cpu_init_tables;
}
3 初始化machine
machine.cc machine_init()
{
automachine_init()
}
automachine.cc automachine_init()
{
machine_register_oldtestmips()
}
machine_register_oldtestmips()在machine_test.cc 中通过machine.h的宏定义:
#define MACHINE_REGISTER(x) void machine_register_ ## x(void)
MACHINE_REGISTER(oldtestmips)
{
MR_DEFAULT(oldtestmips, "Test-machine for MIPS", ARCH_MIPS, MACHINE_TESTMIPS);
machine_entry_add_alias(me, "oldtestmips");
}
machine.h的宏定义
#define MR_DEFAULT(x,name,arch,type) struct machine_entry \
*me = machine_entry_new(name,arch,type); \
me->setup = machine_setup_ ## x; \
me->set_default_cpu = machine_default_cpu_ ## x; \
machine_entry_register(me, arch);
展开为:
void machine_register_oldtestmips(void)
{
struct machine_entry *me = machine_entry_new("Test-machine for MIPS", ARCH_MIPS, MACHINE_TESTMIPS);
me->setup = machine_setup_oldtestmips
me->set_default_cpu = machine_default_cpu_ oldtestmips;
machine_entry_register(me, ARCH_MIPS);
machine_entry_add_alias(me, "oldtestmips");
}
4 初始化虚拟机
emul.cc emul_simple_init()
{
emul_machine_setup(m, extra_argc, extra_argv, 0, NULL);
}
emul.cc emul_machine_setup()
{
创建系统系统内存
machine_memsize_fix(m);
memory_amount = (uint64_t)m->physical_ram_in_mb * 1048576;
m->memory = memory_new(memory_amount, m->arch);
创建cpu管理单元
CHECK_ALLOCATION(m->cpus = (struct cpu **) malloc(sizeof(struct cpu *) * m->ncpus));
memset(m->cpus, 0, sizeof(struct cpu *) * m->ncpus);
m->cpus[i] = cpu_new(m->memory, m, i, m->cpu_name);
向系统内存写入随机数
cpu->memory_rw(cpu, m->memory, i, data, sizeof(data),MEM_WRITE, CACHE_NONE | NO_EXCEPTIONS | PHYSICAL);
添加通用的device
device_add(m, device_names[i]);
machine设置
machine_setup(m);
将要运行的文件load进入虚拟机
file_load(m, m->memory, name_to_load, &entrypoint, m->arch, &gp, &byte_order, &toc);
}
4.1 创建cpu管理单元
cpu.cc cpu_new
{
fp->cpu_new(cpu, mem, machine, cpu_id, cpu_type_name)
初始化table,在mips_cpu_family_init中定义为mips_cpu_init_tables
fp->init_tables(cpu);
}
cpu_new在mips_cpu_family_init时已经设置为mips _cpu_new
cpu_mips.cc mips _cpu_new
{
设置内存访问函数
cpu->memory_rw = mips_memory_rw;
设置指令访问函数
cpu->run_instr = mips32_run_instr;
cpu->instruction_has_delayslot = mips_cpu_instruction_has_delayslot;
设置异常中断
templ.interrupt_assert = mips_cpu_interrupt_assert;
templ.interrupt_deassert = mips_cpu_interrupt_deassert;
interrupt_handler_register(&templ);
设置cp0/cp1
cpu->cd.mips.coproc[0] = mips_coproc_new(cpu, 0);
cpu->cd.mips.coproc[1] = mips_coproc_new(cpu, 1);
设置虚拟地址到物理地址的转换函数
cpu->translate_v2p = translate_v2p_mmu3k;
}
4.2 machine设置
machine.cc machine_setup()
{
me->setup(machine, cpu);
}
setup在machine_init的时候注册,oldtestmips对应的是machine_setup_oldtestmips在machine_test.cc 中通过machine.h的宏定义:
#define MACHINE_SETUP(x) void machine_setup_ ## x(struct machine *machine, struct cpu *cpu)
MACHINE_SETUP(oldtestmips)
{
device_add(machine, tmpstr); 添加于machine相关的device
}
4.3 load文件到虚拟机内
gxemul可以load各种格式的文件,这里只分析load bin文件的过程
file.cc file_load()
{
file_load_raw()
}
file_raw.cc file_load_raw
{
从脚本中分析出load地址和文件名
loadaddr = vaddr = entry = strtoull(filename, NULL, 0);
p2 = p+1;
打开对应要运行的文件
f = fopen(strrchr(filename, ':')+1, "r");
读文件并写入到内存中
len = fread(buf, 1, to_read, f);
m->cpus[0]->memory_rw(m->cpus[0], mem, vaddr, &buf[0], len, MEM_WRITE, NO_EXCEPTIONS);
关闭文件
fclose(f);
}
5 运行machine
emul.cc emul_run()
{
cpu_run_init(emul->machines[j]); 初始化machine内的CPU
timer_start() 启动虚拟机的clock
//循环执行
while(go)
{
timer_poll();
anything = machine_run(emul->machines[j]);
if (anything)
go = 1;
}
timer_stop(); 停止虚拟机clock
cpu_run_deinit(emul->machines[j]);
}
machine.cc machine_run()
{
cpus[i]->run_instr(cpus[i]); 执行指令
machine->tick_functions.f[te](cpus[0], machine->tick_functions.extra[te]); trick
}
文后话:
gxemul的源代码使用了大量的宏,按照宏的模板生成代码,这可能和作者想通过统一的模板来表示不同的CPU架构,能是代码简洁且模块化强。但是大量的宏降低了代码的可读性。基于同样的原因gxemul针对CPU架构的一些处理代码使用了文件自动生成,阅读的时候也要注意。