qemu的板级初始化笔记(以i4ffx为例)

1、DEFINE_I440FX_MACHINE函数宏的定义

cpp 复制代码
#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \
    static void pc_init_##suffix(MachineState *machine) \
    { \
        void (*compat)(MachineState *m) = (compatfn); \
        if (compat) { \
            compat(machine); \
        } \
        pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
                 TYPE_I440FX_PCI_DEVICE); \
    } \
    DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)

2、DEFINE_PC_MACHINE函数宏的定义

cpp 复制代码
#define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \
    static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \
    { \
        MachineClass *mc = MACHINE_CLASS(oc); \
        optsfn(mc); \
        mc->init = initfn; \
        mc->kvm_type = pc_machine_kvm_type; \
    } \
    static const TypeInfo pc_machine_type_##suffix = { \
        .name       = namestr TYPE_MACHINE_SUFFIX, \
        .parent     = TYPE_PC_MACHINE, \
        .class_init = pc_machine_##suffix##_class_init, \
    }; \
    static void pc_machine_init_##suffix(void) \
    { \
        type_register(&pc_machine_type_##suffix); \
    } \
    type_init(pc_machine_init_##suffix)

比如:pc-i440fx-8.1

cpp 复制代码
DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", NULL, pc_i440fx_8_1_machine_options);
DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn)

DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn)
DEFINE_PC_MACHINE(v8_1, "pc-i440fx-8.1", pc_init_v8_1, pc_i440fx_8_1_machine_options)
展开来看
static void pc_init_v8_1(MachineState *machine)
{
    void (*compat)(MachineState *m) = NULL;
    if (compat) {
        compat(machine);
    }
    pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE); 
} 

static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data) \
{
    MachineClass *mc = MACHINE_CLASS(oc);
    pc_i440fx_8_1_machine_options(mc);
    mc->init =pc_init_v8_1;
    mc->kvm_type = pc_machine_kvm_type;
}

static const TypeInfo pc_machine_type_v8_1 = {
    .name       = "pc-i440fx-8.1-machine", // #define TYPE_MACHINE_SUFFIX "-machine"
    .parent     = "generic-pc-machine", // #define TYPE_PC_MACHINE "generic-pc-machine"
    .class_init = pc_machine_v8_1_class_init,
};

static void pc_machine_init_v8_1(void)
{
    type_register(&pc_machine_type_v8_1);
}

type_init(pc_machine_init_v8_1)

qemu启动后板级初始化调用

cpp 复制代码
void machine_run_board_init(MachineState *machine, const char *mem_path, Error **errp)
{
    MachineClass *machine_class = MACHINE_GET_CLASS(machine);
    ObjectClass *oc = object_class_by_name(machine->cpu_type);
    CPUClass *cc;

    /* This checkpoint is required by replay to separate prior clock reading from the other reads, because timer polling functions query clock values from the log. */
    replay_checkpoint(CHECKPOINT_INIT);
    if (!xen_enabled()) {
        /* On 32-bit hosts, QEMU is limited by virtual address space */
        if (machine->ram_size > (2047 << 20) && HOST_LONG_BITS == 32) {
            error_setg(errp, "at most 2047 MB RAM can be simulated");
            return;
        }
    }

    if (machine->memdev) {
        ram_addr_t backend_size = object_property_get_uint(OBJECT(machine->memdev), "size",  &error_abort);
        if (backend_size != machine->ram_size) {
            error_setg(errp, "Machine memory size does not match the size of the memory backend");
            return;
        }
    } else if (machine_class->default_ram_id && machine->ram_size && numa_uses_legacy_mem()) {
        if (object_property_find(object_get_objects_root(), machine_class->default_ram_id)) {
            error_setg(errp, "object name '%s' is reserved for the default RAM backend, it can't be used for any other purposes. Change the object's 'id' to something else",
                machine_class->default_ram_id);
            return;
        }
        if (!create_default_memdev(current_machine, mem_path, errp)) {
            return;
        }
    }

    if (machine->numa_state) {
        numa_complete_configuration(machine);
        if (machine->numa_state->num_nodes) {
            machine_numa_finish_cpu_init(machine);
            if (machine_class->cpu_cluster_has_numa_boundary) {
                validate_cpu_cluster_to_numa_boundary(machine);
            }
        }
    }

    if (!machine->ram && machine->memdev) {
        machine->ram = machine_consume_memdev(machine, machine->memdev);
    }

    /* If the machine supports the valid_cpu_types check and the user specified a CPU with -cpu check here that the user CPU is supported. */
    if (machine_class->valid_cpu_types && machine->cpu_type) {
        int i;
        for (i = 0; machine_class->valid_cpu_types[i]; i++) {
            if (object_class_dynamic_cast(oc, machine_class->valid_cpu_types[i])) {
                /* The user specificed CPU is in the valid field, we are good to go. */
                break;
            }
        }

        if (!machine_class->valid_cpu_types[i]) {
            /* The user specified CPU is not valid */
            error_report("Invalid CPU type: %s", machine->cpu_type);
            error_printf("The valid types are: %s", machine_class->valid_cpu_types[0]);
            for (i = 1; machine_class->valid_cpu_types[i]; i++) {
                error_printf(", %s", machine_class->valid_cpu_types[i]);
            }
            error_printf("\n");
            exit(1);
        }
    }

    /* Check if CPU type is deprecated and warn if so */
    cc = CPU_CLASS(oc);
    if (cc && cc->deprecation_note) {
        warn_report("CPU model %s is deprecated -- %s", machine->cpu_type, cc->deprecation_note);
    }
    if (machine->cgs) {
        /* With confidential guests, the host can't see the real contents of RAM, so there's no point in it trying to merge areas. */
        machine_set_mem_merge(OBJECT(machine), false, &error_abort);

        /*
         * Virtio devices can't count on directly accessing guest memory, so they need iommu_platform=on to use normal DMA
         * mechanisms.  That requires also disabling legacy virtio support for those virtio pci devices which allow it.
         */
        object_register_sugar_prop(TYPE_VIRTIO_PCI, "disable-legacy", "on", true);
        object_register_sugar_prop(TYPE_VIRTIO_DEVICE, "iommu_platform",  "on", false);
    }
    accel_init_interfaces(ACCEL_GET_CLASS(machine->accelerator));
    machine_class->init(machine);
    phase_advance(PHASE_MACHINE_INITIALIZED);
}

i440fx板级初始化pc_init1():

  1. 计算 x86 PC 内存布局
  2. 创建并 realize 所有 CPU
  3. 建立 PCI 根总线和南桥
  4. 初始化 BIOS/RAM/ROM 映射
  5. 建立 PIC/IOAPIC/GSI 中断体系
  6. 创建 ISA/RTC/VGA/NIC/IDE/USB 等基础设备
  1. 挂上 ACPI/SMBus/电源管理能力
cpp 复制代码
/* PC hardware initialisation */
static void pc_init1(MachineState *machine, const char *host_type, const char *pci_type)
{
    PCMachineState *pcms = PC_MACHINE(machine);
    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
    X86MachineState *x86ms = X86_MACHINE(machine);
    MemoryRegion *system_memory = get_system_memory();
    MemoryRegion *system_io = get_system_io();
    PCIBus *pci_bus = NULL;
    ISABus *isa_bus;
    int piix3_devfn = -1;
    qemu_irq smi_irq;
    GSIState *gsi_state;
    BusState *idebus[MAX_IDE_BUS];
    ISADevice *rtc_state;
    MemoryRegion *ram_memory;
    MemoryRegion *pci_memory = NULL;
    MemoryRegion *rom_memory = system_memory;
    ram_addr_t lowmem;
    uint64_t hole64_size = 0;

    // 1. 计算 PC 的物理内存布局
    /*
     * Calculate ram split, for memory below and above 4G.  It's a bit complicated for backward compatibility reasons ...
     *
     *  - Traditional split is 3.5G (lowmem = 0xe0000000).  This is the
     *    default value for max_ram_below_4g now.
     *
     *  - Then, to gigabyte align the memory, we move the split to 3G
     *    (lowmem = 0xc0000000).  But only in case we have to split in
     *    the first place, i.e. ram_size is larger than (traditional)
     *    lowmem.  And for new machine types (gigabyte_align = true)
     *    only, for live migration compatibility reasons.
     *
     *  - Next the max-ram-below-4g option was added, which allowed to
     *    reduce lowmem to a smaller value, to allow a larger PCI I/O
     *    window below 4G.  qemu doesn't enforce gigabyte alignment here,
     *    but prints a warning.
     *
     *  - Finally max-ram-below-4g got updated to also allow raising lowmem,
     *    so legacy non-PAE guests can get as much memory as possible in
     *    the 32bit address space below 4G.
     *
     *  - Note that Xen has its own ram setup code in xen_ram_init(),
     *    called via xen_hvm_init_pc().
     *
     * Examples:
     *    qemu -M pc-1.7 -m 4G    (old default)    -> 3584M low,  512M high
     *    qemu -M pc -m 4G        (new default)    -> 3072M low, 1024M high
     *    qemu -M pc,max-ram-below-4g=2G -m 4G     -> 2048M low, 2048M high
     *    qemu -M pc,max-ram-below-4g=4G -m 3968M  -> 3968M low (=4G-128M)
     */
    if (xen_enabled()) {
        xen_hvm_init_pc(pcms, &ram_memory);
    } else {
        ram_memory = machine->ram;
        if (!pcms->max_ram_below_4g) {
            pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */
        }
        lowmem = pcms->max_ram_below_4g;
        if (machine->ram_size >= pcms->max_ram_below_4g) {
            if (pcmc->gigabyte_align) {
                if (lowmem > 0xc0000000) {
                    lowmem = 0xc0000000;
                }
                if (lowmem & (1 * GiB - 1)) {
                    warn_report("Large machine and max_ram_below_4g (%" PRIu64 ") not a multiple of 1G; possible bad performance.", pcms->max_ram_below_4g);
                }
            }
        }

        if (machine->ram_size >= lowmem) {
            x86ms->above_4g_mem_size = machine->ram_size - lowmem;
            x86ms->below_4g_mem_size = lowmem;
        } else {
            x86ms->above_4g_mem_size = 0;
            x86ms->below_4g_mem_size = machine->ram_size;
        }
    }

    // 2. 初始化 CPU
    pc_machine_init_sgx_epc(pcms);
    x86_cpus_init(x86ms, pcmc->default_cpu_version);

    // 3. 创建 KVM paravirt clock 设备
    if (pcmc->kvmclock_enabled) {
        kvmclock_create(pcmc->kvmclock_create_always);
    }

    // 4. 创建 PCI 根总线和 i440FX 主桥
    if (pcmc->pci_enabled) {
        Object *phb;
        pci_memory = g_new(MemoryRegion, 1);
        memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
        rom_memory = pci_memory;

        phb = OBJECT(qdev_new(host_type));
        object_property_add_child(OBJECT(machine), "i440fx", phb);
        object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, OBJECT(ram_memory), &error_fatal);
        object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, OBJECT(pci_memory), &error_fatal);
        object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, OBJECT(system_memory), &error_fatal);
        object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, OBJECT(system_io), &error_fatal);
        object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, x86ms->below_4g_mem_size, &error_fatal);
        object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, x86ms->above_4g_mem_size, &error_fatal);
        object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, &error_fatal);
        sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal);

        pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0"));
        pci_bus_map_irqs(pci_bus, xen_enabled() ? xen_pci_slot_get_pirq : pc_pci_slot_get_pirq);
        pcms->bus = pci_bus;

        hole64_size = object_property_get_uint(phb, PCI_HOST_PROP_PCI_HOLE64_SIZE, &error_abort);
    }

    // 5. 初始化 guest 信息和 BIOS/固件内存
    pc_guest_info_init(pcms);
    if (pcmc->smbios_defaults) {
        MachineClass *mc = MACHINE_GET_CLASS(machine);
        /* These values are guest ABI, do not change */
        smbios_set_defaults("QEMU", mc->desc, mc->name, pcmc->smbios_legacy_mode, pcmc->smbios_uuid_encoded, pcms->smbios_entry_point_type);
    }

    /* allocate ram and load rom/bios */
    if (!xen_enabled()) {
        pc_memory_init(pcms, system_memory, rom_memory, hole64_size);
    } else {
        assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size);
        pc_system_flash_cleanup_unused(pcms);
        if (machine->kernel_filename != NULL) {
            /* For xen HVM direct kernel boot, load linux here */
            xen_load_linux(pcms);
        }
    }
    // 6. 建立中断体系
    gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled);

    // 7. 创建南桥PIIX3 southbridge / ISA 总线 / RTC
    if (pcmc->pci_enabled) {
        PIIX3State *piix3;
        PCIDevice *pci_dev;
        pci_dev = pci_create_simple_multifunction(pci_bus, -1, TYPE_PIIX3_DEVICE);
        if (xen_enabled()) {
            pci_device_set_intx_routing_notifier( pci_dev, piix_intx_routing_notifier_xen);
            // Xen 支持从 PCI 设备到 IOAPIC 的额外中断路由:总线上每个 PCI 设备的四个引脚也直接连接至 IOAPIC。这些额外路由可通过 ACPI 进行发现。
            pci_bus_irqs(pci_bus, xen_intx_set_irq, pci_dev, XEN_IOAPIC_NUM_PIRQS);
        }
        piix3 = PIIX3_PCI_DEVICE(pci_dev);
        piix3->pic = x86ms->gsi;
        piix3_devfn = piix3->dev.devfn;
        isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0"));
        rtc_state = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), "rtc"));
    } else {
        isa_bus = isa_bus_new(NULL, system_memory, system_io, &error_abort);
        rtc_state = isa_new(TYPE_MC146818_RTC);
        qdev_prop_set_int32(DEVICE(rtc_state), "base_year", 2000);
        isa_realize_and_unref(rtc_state, isa_bus, &error_fatal);
        i8257_dma_init(isa_bus, 0);
        pcms->hpet_enabled = false;
    }
    isa_bus_register_input_irqs(isa_bus, x86ms->gsi);

    // 建立中断体系
    if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) {
        pc_i8259_create(isa_bus, gsi_state->i8259_irq);
    }
    if (pcmc->pci_enabled) {
        ioapic_init_gsi(gsi_state, "i440fx");
    }
    if (tcg_enabled()) {
        x86_register_ferr_irq(x86ms->gsi[13]);
    }

    // 8. 初始化基础外设 VGA,PIT,PS/2 控制器,串口/并口,扬声器,FDC,HPET 等
    pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL);
    assert(pcms->vmport != ON_OFF_AUTO__MAX);
    if (pcms->vmport == ON_OFF_AUTO_AUTO) {
        pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
    }
    pc_basic_device_init(pcms, isa_bus, x86ms->gsi, rtc_state, true, 0x4);
    pc_nic_init(pcmc, isa_bus, pci_bus);
    if (pcmc->pci_enabled) {
        PCIDevice *dev;
        dev = pci_create_simple(pci_bus, piix3_devfn + 1, TYPE_PIIX3_IDE);
        pci_ide_create_devs(dev);
        idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0");
        idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1");
        pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
    }
#ifdef CONFIG_IDE_ISA
    else {
        DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
        int i;
        ide_drive_get(hd, ARRAY_SIZE(hd));
        for (i = 0; i < MAX_IDE_BUS; i++) {
            ISADevice *dev;
            char busname[] = "ide.0";
            dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]);
            
            // IDE 总线的名称,第一条为 ide.0,第二条为 ide.1
            busname[4] = '0' + i;
            idebus[i] = qdev_get_child_bus(DEVICE(dev), busname);
        }
        pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
    }
#endif

    //  9. 初始化 IDE / CMOS / USB
    if (pcmc->pci_enabled && machine_usb(machine)) {
        pci_create_simple(pci_bus, piix3_devfn + 2, TYPE_PIIX3_USB_UHCI);
    }
    if (pcmc->pci_enabled && x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) {
        PCIDevice *piix4_pm;
        smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0);
        piix4_pm = pci_new(piix3_devfn + 3, TYPE_PIIX4_PM);
        qdev_prop_set_uint32(DEVICE(piix4_pm), "smb_io_base", 0xb100);
        qdev_prop_set_bit(DEVICE(piix4_pm), "smm-enabled", x86_machine_is_smm_enabled(x86ms));
        pci_realize_and_unref(piix4_pm, pci_bus, &error_fatal);
        qdev_connect_gpio_out(DEVICE(piix4_pm), 0, x86ms->gsi[9]);
        qdev_connect_gpio_out_named(DEVICE(piix4_pm), "smi-irq", 0, smi_irq);
        pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(piix4_pm), "i2c"));

        /* TODO: Populate SPD eeprom data.  */
        smbus_eeprom_init(pcms->smbus, 8, NULL, 0);
        object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, TYPE_HOTPLUG_HANDLER,
                                 (Object **)&x86ms->acpi_dev, object_property_allow_set_link, OBJ_PROP_LINK_STRONG);
        object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, OBJECT(piix4_pm), &error_abort);
    }

    // 10. 初始化 ACPI 电源管理
    if (machine->nvdimms_state->is_enabled) {
        nvdimm_init_acpi_state(machine->nvdimms_state, system_io, x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms));
    }
}
相关推荐
冰山一脚201311 小时前
CPU的L1、L2、L3缓存笔记
qemu
Shining059611 天前
QEMU 编译开发环境搭建
人工智能·语言模型·自然语言处理·云原生·qemu·vllm·华为昇腾
高铭杰13 天前
Postgresql热迁移pgbench持续读写零中断
postgresql·qemu·neon
高铭杰24 天前
QEMU网络配置1
qemu·vxlan
enjoy嚣士2 个月前
Windows10下安装arm64架构的centos
qemu·aarch64 linux·arm64 linux·arm64 centos
x-cmd3 个月前
[x-cmd] QEMU 10.2.0 发布:虚拟机实时更新与性能飞跃的技术深度解读
安全·qemu·虚拟机·x-cmd
yao000373 个月前
基于QEMU+OpenSBI+edk2的riscv启动流程解析
qemu·riscv·uefi·bios·固件·opensbi
三雷科技3 个月前
qemu-img 使用手册(含详细案例)
qemu