QEMU模拟IP实例
zhilu.zhang
zhilu.zhang
发布于 2023-09-01 / 116 阅读 / 0 评论 / 0 点赞

QEMU模拟IP实例

(1) 添加xxx设备到QEMU

// 这是一个用于在QEMU虚拟机中添加设备的函数。
// 它接受一个指向SigiEVirt结构的指针和一个整数作为参数。
static void sigie_add_xxx(SigiEVirt *s, int id)
{
    // 声明了两个MemoryRegion指针,一个用于表示设备的内存区域,另一个用于表示系统内存。
    MemoryRegion *mr;
    MemoryRegion *sysmem = get_system_memory();

    // 获取指向虚拟机中GIC设备的指针。
    DeviceState *gicdev = DEVICE(&s->apu.gic);

    // 获取基地址,这是一个用于内存映射的地址。
    hwaddr base = base_memmap[id].base;

    // 创建一个包含一个中断的整数数组irqs。
    int irqs[1] = {SIGI_xxx0_IRQ_0};

    // 使用qdev_new函数创建一个名为"xxx"的设备,类型为HOBOT_xxx。
    s->xxx = qdev_new(TYPE_HOBOT_xxx);

    // 实例化并释放"xxx"设备,并处理可能出现的致命错误。
    sysbus_realize_and_unref(SYS_BUS_DEVICE(s->xxx), &error_fatal);

    // 获取"xxx"设备的内存映射IO区域,并将其赋值给mr。
    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(s->xxx[i]), 0);

    // 将"xxx"设备的内存映射IO区域添加为系统内存区域的子区域。
    memory_region_add_subregion(sysmem, base, mr);

    // 连接"xxx"设备的第一个中断(IRQ 0)到GIC设备的输入GPIO中,
    // 以便在中断触发时触发中断处理程序。
    sysbus_connect_irq(SYS_BUS_DEVICE(s->xxx), 0, qdev_get_gpio_in(gicdev, irqs[i]));
}
// 这是一个用于虚拟机设备实例化的函数。
static void sigi_virt_realize(DeviceState *dev, Error **errp)
{
    // 将传递给函数的设备状态指针强制转换为SigiEVirt结构的指针,方便后续操作。
    SigiEVirt *s = SIGIE_VIRT(dev);
................
    // 调用sigie_add_xxx函数,将一个名为"xxx"的设备添加到虚拟机中。
    // VIRT_xxx是一个参数,用于标识要添加的设备的类型。
    sigie_add_xxx(s, VIRT_xxx0);
................
}

(2) 定义地址空间中断等资源

static const MemMapEntry base_memmap[] = {
................
    [VIRT_xxx] =               { 0x37C80000, 0x00010000 },
................
}
//在虚拟soc的结构体中添加设备描述
DeviceState *xxx[1];

struct DeviceState {
    /*< private >*/
    Object parent_obj; // 父对象,用于管理设备对象的基本属性和行为

    /*< public >*/

    char *id; // 设备的唯一标识符
    char *canonical_path; // 设备的规范路径
    bool realized; // 表示设备是否已经实例化
    bool pending_deleted_event; // 表示是否有待处理的删除事件
    int64_t pending_deleted_expires_ms; // 待处理删除事件的超时时间(毫秒)
    QDict *opts; // 设备的配置选项(以键值对的形式存储)
    int hotplugged; // 表示设备是否是热插拔设备
    bool allow_unplug_during_migration; // 表示是否允许在迁移期间卸载设备
    BusState *parent_bus; // 设备所属的总线对象
    QLIST_HEAD(, NamedGPIOList) gpios; // 与设备关联的GPIO列表
    QLIST_HEAD(, NamedClockList) clocks; // 与设备关联的时钟列表
    QLIST_HEAD(, BusState) child_bus; // 与设备关联的子总线列表
    int num_child_bus; // 子总线的数量
    int instance_id_alias; // 实例ID的别名
    int alias_required_for_version; // 版本所需的别名
    ResettableState reset; // 设备的复位状态
    GSList *unplug_blockers; // 阻止设备卸载的列表
};

(3)设备头文件描述

#include "qemu/fifo64.h"            // 包含FIFO64相关的头文件
#include "SOC/utils/SOC_image_buf.h" // 包含SOC_image_buf相关的头文件

#define TYPE_SOC_xxx "SOC_xxx"  // 定义了设备类型的名称
#define SOC_xxx_OBJECT(obj) OBJECT_CHECK(SOCxxxState, (obj), TYPE_SOC_xxx) // 检查对象是否是SOCxxxState类型
#define SOC_xxx_CLASS(klass) OBJECT_CLASS_CHECK(SOCxxxClass, (klass), TYPE_SOC_xxx) // 检查类是否是SOCxxxClass类型
#define SOC_xxx_GET_CLASS(obj) OBJECT_GET_CLASS(SOCxxxClass, (obj), TYPE_SOC_xxx) // 获取对象的类

#define SOC_xxx_ADDR_FIFO_SIZE (16)  // 定义了地址FIFO的大小

/* xxx ddr start reg */
#define xxx_DDR_START (1 << 0) // 定义了xxx DDR启动标志位

/* xxx int status reg */
enum xxx_interrupt_map {
    INTR_xxx_BUSY,
    INTR_xxx_ERROR,
    INTR_xxx_CONF_ERROR = 8,
    INTR_xxx_USER_ABORT,
    INTR_xxx_AXI_READER_ERROR,
    INTR_xxx_AXI_WRITER_ERROR,
    INTR_xxx_UNALIGNED_ACCESS,
    INTR_xxx_INCOMPATIBLE_CONF,
};
// 定义了xxx设备的中断映射枚举,表示不同的中断类型

enum xxx_reg {
    xxx_ID,
    xxx_CONFIG_ADDR = 0x10 >> 2,
    xxx_CONFIG_SIZE,
    xxx_STATUS,
    xxx_PROCESS_CONFIG,
    xxx_AXI_SETTING_CONFIG_READER,
    xxx_AXI_SETTING_TILE_READER,
    xxx_AXI_SETTING_TILE_WRITER,
    xxx_INT_STATUS = 0x200 >> 2,
    xxx_INT_MASK = 0x204 >> 2,
    NUM_OF_xxx_REG,
};
// 定义了xxx设备的寄存器映射枚举,表示不同寄存器的偏移量

typedef struct SOCxxxState {
    SysBusDevice parent_obj; // SOCxxxState结构体的父对象

    MemoryRegion iomem; // 内存区域对象,用于访问设备的内存映射区域
    qemu_irq irq; // 设备的中断请求(IRQ)线
    uint32_t regs[NUM_OF_xxx_REG]; // 存储xxx设备寄存器的数组
    uint32_t xxx_int_mask; // xxx设备的中断屏蔽掩码
    uint64_t hva_ram_ptr; // 虚拟地址指针,指向设备的RAM地址
    uint64_t gva_ram_addr; // 全局虚拟地址指针,指向设备的RAM地址

    QemuThread thread; // QEMU线程对象,用于处理设备相关的线程操作
    QemuMutex thr_mutex; // QEMU互斥锁,用于线程同步
    QemuCond thr_cond; // QEMU条件变量,用于线程同步
    QemuSemaphore hw_sem; // QEMU信号量,用于线程同步

    Fifo64 addr_fifo[SOC_xxx_NUM]; // 64位FIFO队列数组,用于存储地址

} SOCxxxState;
// 定义了SOCxxxState结构体,用于表示xxx SOC设备的状态和属性

typedef struct SOCxxxClass {
    SysBusDeviceClass parent_class; // SOCxxxClass结构体的父类
} SOCxxxClass;
// 定义了SOCxxxClass结构体,用于表示xxx SOC设备的类信息

(4)设备相关函数

#include <stdio.h>
#include <stdlib.h>
#include "qemu/osdep.h"

#define debug_log 0 // 调试标志位

// 用于将物理地址转换为主机虚拟地址的函数
static void *raw_gpa2hva(MemoryRegion **p_mr, hwaddr addr, Error **errp)
{
    // 查找地址对应的内存区域
    MemoryRegionSection mrs = memory_region_find(get_system_memory(), addr, 1);

    // 检查内存区域是否为RAM或ROMD类型
    if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) {
        printf("Memory at address 0x%lx is not RAM\n", addr);
        memory_region_unref(mrs.mr); // 释放内存区域引用
        return NULL;
    }
    *p_mr = mrs.mr; // 将内存区域指针传递给调用者
    return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); // 返回主机虚拟地址
}

// 将物理地址转换为主机虚拟地址的包装函数
static uint64_t gpa2hva(hwaddr addr)
{
    Error *local_err = NULL;
    MemoryRegion *mr = NULL;
    void *ptr;
    ptr = raw_gpa2hva(&mr, addr, &local_err); // 调用原始函数
    memory_region_unref(mr); // 释放内存区域引用
    return (uint64_t)ptr; // 返回主机虚拟地址
}

// 初始化xxx设备的配置信息
static void SOC_ayers_xxx_init_config(
    uint32_t *xxx_reg, hbsim_xxx_if *xxx_config)
{
    // 初始化xxx_config结构体,将xxx设备的配置信息填充到xxx_config中
}

// 更新xxx设备的中断状态
static void SOC_ayers_xxx_update_interrupt(
    SOCxxxState *xxx_state, uint32_t bit)
{
    // 更新xxx设备的中断状态,并通知QEMU的中断管理系统
}

// 从文件中读取数据
static int read_file(const char *filename, char *addr, uint32_t size)
{
    FILE *Fd = NULL;
    Fd = fopen(filename, "r"); // 打开文件以供读取
    fread(addr, size, 1, Fd); // 从文件中读取数据
    fflush(Fd); // 刷新文件流以确保数据写入文件
    fclose(Fd); // 关闭文件
    return size; // 返回读取的数据大小
}

// 将数据写入文件
static int dumpFile(const char *filename, char *srcBuf, unsigned int size)
{
    FILE *yuvFd = NULL;
    yuvFd = fopen(filename, "w"); // 打开文件以供写入
    fwrite(srcBuf, size, 1, yuvFd); // 将数据写入文件
    fflush(yuvFd); // 刷新文件流以确保数据写入文件
    fclose(yuvFd); // 关闭文件
    return 0; // 返回写入结果
}

// 处理xxx设备的数据
static void hbsimxxx_process(hbsim_xxx_if xxx_config)
{
    // 处理xxx设备的数据(占位函数,具体实现可能在其他地方)
}

// 处理xxx设备的数据并更新中断状态
static void SOC_ayers_xxx_process(SOCxxxState *xxx_state)
{
    hbsim_xxx_if xxx_config;
    SOC_ayers_xxx_init_config(xxx_state->regs, &xxx_config); // 初始化配置信息
    hbsimxxx_process(xxx_config); // 处理xxx设备的数据
}

// xxx设备的处理线程函数
static void *SOC_ayers_xxx_process_thread(void *param)
{
    SOCxxxState *xxx_state = (SOCxxxState *)param;
    while (1) {
        qemu_sem_wait(&xxx_state->hw_sem); // 等待信号量
        SOC_ayers_xxx_process(xxx_state); // 处理xxx设备的数据
        SOC_ayers_xxx_update_interrupt(xxx_state, 1); // 更新中断状态
    }
    return NULL;
}

// 读取xxx设备的寄存器值
static uint64_t SOC_ayers_xxx_read(void *opaque, hwaddr offset, unsigned size)
{
    SOCxxxState *xxx_state = (SOCxxxState *)opaque;
    uint32_t index = offset >> 2; // 计算寄存器索引
    uint64_t value = xxx_state->regs[index]; // 从寄存器数组中读取值

    switch (index) {
    case xxx_INT_STATUS:
        qemu_set_irq(xxx_state->irq, 0); // 清除中断状态并通知QEMU
        break;
    default:
        break;
    }

    return value; // 返回读取的寄存器值
}

// 写入xxx设备的寄存器值
static void SOC_ayers_xxx_write(
    void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
    SOCxxxState *xxx_state = (SOCxxxState *)opaque;
    uint32_t index = offset >> 2; // 计算寄存器索引

    xxx_state->regs[index] = value; // 将值写入寄存器

    switch (index) {
    case xxx_PROCESS_CONFIG:
        if (xxx_state->regs[index] & xxx_DDR_START) {
            xxx_state->regs[index] = 0;
            qemu_sem_post(&xxx_state->hw_sem); // 发送信号以启动处理线程
        }
        break;
    case xxx_INT_STATUS:
        xxx_state->regs[index] &= ~value; // 清除特定位的中断状态
        break;
    default:
        break;
    }
}

// 定义设备的内存操作函数集合
static const MemoryRegionOps SOC_ayers_xxx_ops = {
    .read = SOC_ayers_xxx_read, // 读取函数
    .write = SOC_ayers_xxx_write, // 写入函数
    .endianness = DEVICE_NATIVE_ENDIAN, // 设备的字节序
};

// 初始化设备的寄存器
static void SOC_ayers_xxx_init_regs(SOCxxxState *xxx_state)
{
    xxx_state->regs[xxx_INT_MASK] = 0; // 初始化寄存器中断掩码
    return;
}

// 初始化设备实例
static void SOC_ayers_xxx_instance_init(Object *obj)
{
    SOCxxxState *xxx_state = SOC_xxx_OBJECT(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);

    // 初始化设备内存区域
    memory_region_init_io(&xxx_state->iomem, obj, &SOC_ayers_xxx_ops, sbd,
        TYPE_SOC_xxx, 0x1000);
    sysbus_init_mmio(sbd, &xxx_state->iomem); // 初始化设备的MMIO接口
    sysbus_init_irq(sbd, &xxx_state->irq); // 初始化设备的IRQ接口

    SOC_ayers_xxx_init_regs(xxx_state); // 初始化设备的寄存器

    qemu_sem_init(&xxx_state->hw_sem, 0); // 初始化设备的信号量
}

// 实例化设备
static void SOC_ayers_xxx_realize(DeviceState *dev, Error **errp)
{
    SOCxxxState *xxx_state = SOC_xxx_OBJECT(dev);
    char cmd_line[128] = {0};
    int32_t ret;
    qemu_mutex_init(&xxx_state->thr_mutex); // 初始化互斥锁
    qemu_cond_init(&xxx_state->thr_cond); // 初始化条件变量
    qemu_thread_create(&xxx_state->thread, TYPE_SOC_xxx,
        SOC_ayers_xxx_process_thread, xxx_state, QEMU_THREAD_JOINABLE); // 创建设备处理线程
}

// 取消实例化设备
static void SOC_ayers_xxx_unrealize(DeviceState *dev)
{
    SOCxxxState *xxx_state = SOC_xxx_OBJECT(dev);

    qemu_mutex_lock(&xxx_state->thr_mutex);

    qemu_mutex_unlock(&xxx_state->thr_mutex);
    qemu_cond_signal(&xxx_state->thr_cond); // 发送信号以唤醒等待的线程
    qemu_thread_join(&xxx_state->thread); // 等待线程退出

    qemu_cond_destroy(&xxx_state->thr_cond); // 销毁条件变量
    qemu_mutex_destroy(&xxx_state->thr_mutex); // 销毁互斥锁
}

// 定义设备属性列表
static Property SOC_ayers_xxx_properties[] = {
    DEFINE_PROP_UINT64("hva_ram_ptr", SOCxxxState, hva_ram_ptr, 0),
    DEFINE_PROP_UINT64("gva_ram_addr", SOCxxxState, gva_ram_addr, 0),
    DEFINE_PROP_END_OF_LIST(),
};

// 初始化设备类
static void SOC_ayers_xxx_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->realize = SOC_ayers_xxx_realize; // 设备实例化函数
    dc->unrealize = SOC_ayers_xxx_unrealize; // 设备取消实例化函数
    device_class_set_props(dc, SOC_ayers_xxx_properties); // 设置设备属性列表

    return;
}

// 定义设备类型信息
static const TypeInfo SOC_ayers_xxx_info = {
    .name = TYPE_SOC_xxx, // 设备类型名称
    .parent = TYPE_SYS_BUS_DEVICE, // 父类设备类型
    .instance_size = sizeof(SOCxxxState), // 实例大小
    .instance_init = SOC_ayers_xxx_instance_init, // 实例初始化函数
    .class_size = sizeof(SOCxxxClass), // 类大小
    .class_init = SOC_ayers_xxx_class_init, // 类初始化函数
};

// 注册设备类型
static void SOC_ayers_xxx_register(void)
{
    type_register_static(&SOC_ayers_xxx_info); // 注册设备类型信息
}

type_init(SOC_ayers_xxx_register); // 初始化设备类型