Skip to content

QEMU

QEMU is a generic and open source machine emulator and virtualizer

显然,QEMU有两个用途

  • 作为性能非常不错的模拟器,看起来这对于SOC开发的场景,在调试整套系统软件时是一个不错的选择
  • 作为虚拟机,qemu包含了虚拟化的一套内容,可以配合KVM来实现,这部分内容暂时不太了解

本文只针对QEMU作为模拟器的场景, 有些技术内容翻译自官方文档[https://qemu-project.gitlab.io/qemu/]

整体实现

QEMU采取了动态转换机制,会将target代码翻译成名为TCG的IR,IR又会翻译成host代码,代码按照 TranslationBlock分块进行转换和执行。舍得舍得,有舍必有得,有得必有舍。速度提升了,可调试性就下降了。 QEMU跟一般模拟器相比,调试起来更麻烦,没法轻松分析指令行为,比如说想单步运行,实时查看寄存器状态, 交互式的调试,这种QEMU做不到,它为的还是更快。

从模拟器的角度来看,我们主要关注QEMU的如下几个方面是如何实现的。

  • 各个部分代码功能。了解代码层次结构划分
  • 整体流程。从顶层来看,整体的流程。了解QEMU大概是如何工作的。
  • 指令执行过程。包含反汇编过程,了解这个过程,从中找到一些调试手段。
  • 对于各个被模拟的设备的管理
  • host调试手段
  • target调试手段

至于其涉及的虚拟化部分,这部分太大,暂时也对此没有什么了解,后续若在工作中需要用到再去研究,毕竟目前这不是我感兴趣的点。

除此之外,在编程语言方面,我也有疑惑。既然QEMU费尽心机,把C语言尽可能做的像C++, 那么为什么不干脆使用C++呢?

代码结构

先来从整个目录的角度来分析一下各部分代码的功能。整体代码结构如下

目录 功能
bsd-user 支持BSD系统,通过这一层,可以直接运行BSD应用程序
linux-user 支持linux系统,通过这一层,可以直接运行linux应用
hw 实现了对各种外设的模拟
QOM QEMU的对象管理,用面向对象的思想实现了一套QEMU管理对象的抽象和封装,各个被模拟的模块都是一个QOM子类
softmmu
target

我们可以看到,主目录有audio目录,在hw目录下面,也有audio目录,这两个目录的功能分别是什么?

设备管理之QOM

可以作为学习面向对象原理的材料,把一个不支持面向对象的C语言,封装成支持面向对象的C语言,这种封装和使用方法,值得学习,通过学习,应该会对面向对象有更深的理解。(当然,这真的有意义吗? c++ 或者python它不香吗?)

在qemu的docs/devel/qom.rst中,包含了难得的对QOM的介绍。QEMU Object Model 提供了一个框架,基于这个框架,使用者可以把自己添加类型做注册和实例化。

TypeInfo 和 TypeImpl

struct TypeInfo
{
    const char *name;
    const char *parent;

    size_t instance_size;
    size_t instance_align;
    void (*instance_init)(Object *obj);
    void (*instance_post_init)(Object *obj);
    void (*instance_finalize)(Object *obj);

    bool abstract;
    size_t class_size;

    void (*class_init)(ObjectClass *klass, void *data);
    void (*class_base_init)(ObjectClass *klass, void *data);
    void *class_data;

    InterfaceInfo *interfaces;
};

struct TypeImpl
{
    const char *name;

    size_t class_size;

    size_t instance_size;
    size_t instance_align;

    void (*class_init)(ObjectClass *klass, void *data);
    void (*class_base_init)(ObjectClass *klass, void *data);

    void *class_data;

    void (*instance_init)(Object *obj);
    void (*instance_post_init)(Object *obj);
    void (*instance_finalize)(Object *obj);

    bool abstract;

    const char *parent;
    TypeImpl *parent_type;

    ObjectClass *class;

    int num_interfaces;
    InterfaceImpl interfaces[MAX_INTERFACES];
};

这两者定义基本相同,但

以RISCV CPU来举例说明

static const TypeInfo riscv_cpu_type_infos[] = {
    {
        .name = TYPE_RISCV_CPU,
        .parent = TYPE_CPU,
        .instance_size = sizeof(RISCVCPU),
        .instance_align = __alignof__(RISCVCPU),
        .instance_init = riscv_cpu_init,
        .abstract = true,
        .class_size = sizeof(RISCVCPUClass),
        .class_init = riscv_cpu_class_init,
    },
    DEFINE_CPU(TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init),
    DEFINE_CPU(TYPE_RISCV_CPU_BASE64,           rv64_base_cpu_init),
    DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51,       rv64_sifive_e_cpu_init),
    DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54,       rv64_sifive_u_cpu_init),
};

DEFINE_TYPES(riscv_cpu_type_infos)

调试方法

ctrl + A / X 退出

ctrl + A / C 监控模式

-D log文件

-d 调试模块开关

代码细节

// cpu线程
tcg_cpu_thread_fn() //tcg
    cpu_exec_step_atomic()
        tb_gen_code()
        cpu_tb_exec()

/* 生成(gen)过程 */

tb_gen_code()
    gen_intermediate_code()
    trace_translate_block()
    tcg_gen_code()

gen_intermediate_code() //arch
    translator_loop(TranslatorOps *ops) //tcg
        ops->translate_insn() : riscv_tr_translate_insn()

riscv_tr_translate_insn()
    decode_opc()
        decode_insn32() // 自动生成,在编译阶段生成的<decode-insn32.c.inc>
            trans_xxxx() //insn_trans目录下的各个trans_xxx_c.inc文件
                tcg_gen_xxxx()
                gen_xxxx()

gen_helper_xxxx()
    tcg_gen_callN(helper_xxxx(), .....)


gen_helper_tlb_flush()



/* 执行过程 */