You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

自定义QEMU DRAM设备初始化触发memory_region_add_subregion_common断言失败问题排查

自定义QEMU DRAM设备初始化触发memory_region_add_subregion_common断言失败问题排查

看起来你在给QEMU的virt机器加自定义DRAM设备时踩了内存区域管理的坑,我来帮你一步步捋清楚这个断言失败的问题:

一、先搞懂这个断言的核心含义

!subregion->container这个断言触发,说白了就是:你要添加的子内存区域,已经被挂载到某个父容器内存区域里了。QEMU的内存区域规则很明确:同一个内存区域不能被重复挂载到不同父容器,也不能多次挂载到同一个容器,否则就会触发这个保护断言。

二、从你的代码逻辑里排查问题点

从你贴的machvirt_init修改内容来看,有几个关键环节可能出了问题:

1. 自定义DRAM设备的初始化逻辑可能存在重复挂载

你的custom_dram_device_init函数里,是不是已经把创建好的自定义内存区域添加到了sysmem(或者其他容器)里,但后续virt机器的初始化流程中,又有代码尝试再次挂载这个区域?

  • 赶紧去检查custom_dram_device_init的实现:
    • 如果里面已经调用过memory_region_add_subregion(sysmem, 自定义地址, custom_ram),那一定要确保这个操作只执行一次,而且后续没有其他代码再把custom_ram往别的容器里塞。
    • 另外,你替换了默认的machine->ram,自定义DRAM的地址范围、大小绝对不能和virt机器自带的其他设备(比如GIC、UART这些)的内存区域冲突,否则也可能触发挂载异常。

2. 处理默认RAM的时机和方式不对

你在machvirt_init最开头就把machine->ram设为NULL,但virt机器的初始化流程里,早期的一些逻辑(比如KVM/HVF的内存映射计算)可能已经对machine->ram做了绑定操作。直接清空它可能会留下残留的挂载关系。

  • 调整处理逻辑的时机和方式:
    不要上来就清空默认RAM,先等virt机器的基础内存结构初始化完成,比如把这段代码移到vms->memmap初始化之后,并且在清空machine->ram前,先把它从系统内存容器里卸载:
    // 先等vms->memmap初始化完成
    if (!vms->memmap) {
        Object *cpuobj;
        ARMCPU *armcpu;
        int pa_bits;
    
        cpuobj = object_new(possible_cpus->cpus[0].type);
        armcpu = ARM_CPU(cpuobj);
        pa_bits = arm_pamax(armcpu);
        object_unref(cpuobj);
    
        virt_set_memmap(vms, pa_bits);
    }
    
    // 现在再处理默认RAM的禁用
    if (machine->ram) {
        error_report("Standard RAM disabled - Using custom DRAM only");
        // 关键:先把默认RAM从系统内存容器里卸载
        memory_region_del_subregion(get_system_memory(), machine->ram);
        machine->ram = NULL;
    }
    
    // 最后再初始化自定义DRAM设备
    custom_dram_device_init(machine);
    

3. 自定义DRAM内存区域的初始化不规范

检查custom_dram_device_init里创建内存区域的代码:

  • 如果你用memory_region_init_ram创建自定义RAM,一定要正确设置owner对象,比如绑定到当前machine对象:
    MemoryRegion *custom_ram = g_new(MemoryRegion, 1);
    Error *err = NULL;
    memory_region_init_ram(custom_ram, OBJECT(machine), "custom-dram", 你的DRAM大小, &err);
    if (err) {
        error_report_err(err);
        exit(1);
    }
    // 只调用一次挂载操作
    memory_region_add_subregion(get_system_memory(), 你的DRAM起始地址, custom_ram);
    
  • 绝对不能对同一个custom_ram对象多次调用memory_region_add_subregion

三、实用调试技巧帮你定位

如果还是找不到问题,给你两个调试小技巧:

  1. custom_dram_device_init里,创建完自定义内存区域后,打印它的container指针:
    g_debug("custom_ram container address: %p", custom_ram->container);
    
    如果打印出来不是NULL,说明这个区域已经被提前挂载了,顺着调用栈找是谁干的就行。
  2. 用GDB跑QEMU,在memory_region_add_subregion_common函数打个断点,触发断言时看调用栈,就能清楚看到是哪个流程在尝试重复挂载内存区域。

总的来说,这个问题本质就是QEMU内存区域的挂载冲突,核心要保证:自定义DRAM区域只被挂载一次,处理默认RAM时先卸载再清空,别留下残留的容器绑定关系。

火山引擎 最新活动