Gxemul 运行原理简述

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_newmips_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);
}

setupmachine_init的时候注册,oldtestmips对应的是machine_setup_oldtestmipsmachine_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架构的一些处理代码使用了文件自动生成,阅读的时候也要注意。













你可能感兴趣的:(Gxemul 运行原理简述)