【QEMU系统分析之实例篇(十四)】

系列文章目录

第十四章 QEMU系统仿真的机器创建分析实例


文章目录


前言

本文以 QEMU 8.2.2 为例,分析其作为系统仿真工具的工作过程,并为读者展示各种 QEMU 系统仿真的启动配置实例。

本文读者需要具备一定的 QEMU 系统仿真使用经验,并对 C 语言编程有一定了解。


一、QEMU是什么?

QEMU 是一个通用且开源的机器模拟器和虚拟机。

其官方主页是:https://www.qemu.org/


二、QEMU系统仿真的机器创建分析实例

1.系统仿真的命令行参数

QEMU 作为系统仿真工具,其入口代码在 system/main.c 文件中,初始化函数 qemu_init() 的实现在 system/vl.c 文件中。

前文完成创建目标机器的过程分析,本文将继续后续运行过程的分析,读者需要对 QEMU 系统启动过程的程序代码有所了解,相关内容可以参考《QEMU系统分析之启动篇》系列文章。

bash 复制代码
..\qemu\8.2.2-qkd\qemu-system-x86_64.exe -cpu "Penryn" -M  "q35,accel=whpx,smm=off" -m "6G" -display "sdl" -audio "sdl,model=hda" -vga "std" -L "data"

2.完成早期后端驱动的设置工作

这部分代码在 system/vl.c 文件中,实现如下:

c 复制代码
int qemu_init(int argc, char **argv)
{
...
    qemu_create_early_backends();
...
}

前文分析了创建后端驱动过程中控制台和字符设备的创建过程,本文继续完成块设备和音频设备驱动的创建过程。


qemu_create_early_backends()

函数 qemu_create_early_backends() 代码如下:

c 复制代码
static void qemu_create_early_backends(void)
{
...
    /*
     * Note: we need to create audio and block backends before
     * setting machine properties, so they can be referred to.
     */
    configure_blockdev(&bdo_queue, machine_class, snapshot);
    audio_init_audiodevs();
    if (default_audio) {
        audio_create_default_audiodevs();
    }
}

首先我们对块设备后端驱动进行配置。


configure_blockdev();

代码如下:

c 复制代码
static void configure_blockdev(BlockdevOptionsQueue *bdo_queue,
                               MachineClass *machine_class, int snapshot)
{
    /*
     * If the currently selected machine wishes to override the
     * units-per-bus property of its default HBA interface type, do so
     * now.
     */
    if (machine_class->units_per_default_bus) {
        override_max_devs(machine_class->block_default_type,
                          machine_class->units_per_default_bus);
    }

    /* open the virtual block devices */
    while (!QSIMPLEQ_EMPTY(bdo_queue)) {
        BlockdevOptionsQueueEntry *bdo = QSIMPLEQ_FIRST(bdo_queue);

        QSIMPLEQ_REMOVE_HEAD(bdo_queue, entry);
        loc_push_restore(&bdo->loc);
        qmp_blockdev_add(bdo->bdo, &error_fatal);
        loc_pop(&bdo->loc);
        qapi_free_BlockdevOptions(bdo->bdo);
        g_free(bdo);
    }
    if (snapshot) {
        qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot,
                          NULL, NULL);
    }
    if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func,
                          &machine_class->block_default_type, &error_fatal)) {
        /* We printed help */
        exit(0);
    }

    default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2,
                  CDROM_OPTS);
    default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);
    default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);

}

audio_init_audiodevs()

代码如下:

c 复制代码
void audio_init_audiodevs(void)
{
    AudiodevListEntry *e;

    QSIMPLEQ_FOREACH(e, &audiodevs, next) {
        audio_init(e->dev, &error_fatal);
    }
}

对 audiodevs 中的每个音频设备调用函数 audio_init() 完成初始化。

函数 audio_init() 代码如下:

c 复制代码
/*
 * if we have dev, this function was called because of an -audiodev argument =>
 *   initialize a new state with it
 * if dev == NULL => legacy implicit initialization, return the already created
 *   state or create a new one
 */
static AudioState *audio_init(Audiodev *dev, Error **errp)
{
    static bool atexit_registered;
    int done = 0;
    const char *drvname;
    VMChangeStateEntry *vmse;
    AudioState *s;
    struct audio_driver *driver;

    s = g_new0(AudioState, 1);

    QLIST_INIT (&s->hw_head_out);
    QLIST_INIT (&s->hw_head_in);
    QLIST_INIT (&s->cap_head);
    if (!atexit_registered) {
        atexit(audio_cleanup);
        atexit_registered = true;
    }

    s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);

    if (dev) {
        /* -audiodev option */
        s->dev = dev;
        drvname = AudiodevDriver_str(dev->driver);
        driver = audio_driver_lookup(drvname);
        if (driver) {
            done = !audio_driver_init(s, driver, dev, errp);
        } else {
            error_setg(errp, "Unknown audio driver `%s'", drvname);
        }
        if (!done) {
            goto out;
        }
    } else {
        assert(!default_audio_state);
        for (;;) {
            AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);
            if (!e) {
                error_setg(errp, "no default audio driver available");
                goto out;
            }
            s->dev = dev = e->dev;
            QSIMPLEQ_REMOVE_HEAD(&default_audiodevs, next);
            g_free(e);
            drvname = AudiodevDriver_str(dev->driver);
            driver = audio_driver_lookup(drvname);
            if (!audio_driver_init(s, driver, dev, NULL)) {
                break;
            }
            qapi_free_Audiodev(dev);
            s->dev = NULL;
        }
    }

    if (dev->timer_period <= 0) {
        s->period_ticks = 1;
    } else {
        s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
    }

    vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
    if (!vmse) {
        dolog ("warning: Could not register change state handler\n"
               "(Audio can continue looping even after stopping the VM)\n");
    }

    QTAILQ_INSERT_TAIL(&audio_states, s, list);
    QLIST_INIT (&s->card_head);
    vmstate_register_any(NULL, &vmstate_audio, s);
    return s;

out:
    free_audio_state(s);
    return NULL;
}

audio_create_default_audiodevs()

最后,如果设置了 default_audio,调用函数 audio_create_default_audiodevs() 创建默认音频设备,代码如下:

c 复制代码
void audio_create_default_audiodevs(void)
{
    for (int i = 0; audio_prio_list[i]; i++) {
        if (audio_driver_lookup(audio_prio_list[i])) {
            QDict *dict = qdict_new();
            Audiodev *dev = NULL;
            Visitor *v;

            qdict_put_str(dict, "driver", audio_prio_list[i]);
            qdict_put_str(dict, "id", "#default");

            v = qobject_input_visitor_new_keyval(QOBJECT(dict));
            qobject_unref(dict);
            visit_type_Audiodev(v, NULL, &dev, &error_fatal);
            visit_free(v);

            audio_define_default(dev, &error_abort);
        }
    }
}

3.调试输出

首先,添加跟踪调试信息,修改后的代码如下:

c 复制代码
```c
static void qemu_create_early_backends(void)
{
	...
    huedbg_flag = 1;
    HUEDBG("\n");
    huedbg_dump_device_configs(2);
    HUEDBG("\n");
    qemu_create_early_backends();
    HUEDBG("\n");
    huedbg_dump_device_configs(2);
    HUEDBG("\n");
    huedbg_flag = 0;
    ...
}

运行后,输出信息如下:

bash 复制代码

总结

以上分析了系统初始化过程中创建早期后端驱动的过程。

相关推荐
威哥爱编程8 小时前
【鸿蒙开发实战篇】鸿蒙跨设备的碰一碰文件分享
harmonyos·arkts·arkui
威哥爱编程8 小时前
【鸿蒙开发实战篇】实现锁屏沉浸实况窗案例
harmonyos·arkts·arkui
威哥爱编程8 小时前
【鸿蒙开发实战篇】基于AVPlayer播放网络视频案例
harmonyos·arkts·arkui
无垠的广袤8 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
威哥爱编程8 小时前
【鸿蒙开发实战篇】实现剪切板复制粘贴的功能
harmonyos·arkts·arkui
威哥爱编程9 小时前
【鸿蒙开发实战篇】鸿蒙6 AI智能体集成实战
harmonyos·arkts·arkui
威哥爱编程9 小时前
【鸿蒙开发实战篇】鸿蒙开发中如何利用代码检查工具(codelinter)的技巧和经验
harmonyos·arkts·arkui
威哥爱编程9 小时前
【鸿蒙开发实战篇】鸿蒙6开发中CANN Kit十大常见问题与解决方案
harmonyos·arkts·arkui
雲烟10 小时前
嵌入式设备EMC安规检测参考
网络·单片机·嵌入式硬件
泽虞10 小时前
《STM32单片机开发》p7
笔记·stm32·单片机·嵌入式硬件