1、qxl前端驱动1.1、qxl驱动通过probe函数初始化qxl设备。static int qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct qxl_device *qdev; int ret; if (pdev-revision 4) { DRM_ERROR(qxl too old, doesnt support client_monitors_config, use xf86-video-qxl in user mode); return -EINVAL; /* TODO: ENODEV ? */ } qdev devm_drm_dev_alloc(pdev-dev, qxl_driver, struct qxl_device, ddev); if (IS_ERR(qdev)) { pr_err(Unable to init drm dev); return -ENOMEM; } ret pci_enable_device(pdev); if (ret) return ret; ret drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, qxl); if (ret) goto disable_pci; if (is_vga(pdev) pdev-revision 5) { ret vga_get_interruptible(pdev, VGA_RSRC_LEGACY_IO); if (ret) { DRM_ERROR(cant get legacy vga ioports\n); goto disable_pci; } } ret qxl_device_init(qdev, pdev); if (ret) goto put_vga; ret qxl_modeset_init(qdev); if (ret) goto unload; drm_kms_helper_poll_init(qdev-ddev); /* Complete initialization. */ ret drm_dev_register(qdev-ddev, ent-driver_data); if (ret) goto modeset_cleanup; drm_fbdev_generic_setup(qdev-ddev, 32); return 0; modeset_cleanup: qxl_modeset_fini(qdev); unload: qxl_device_fini(qdev); put_vga: if (is_vga(pdev) pdev-revision 5) vga_put(pdev, VGA_RSRC_LEGACY_IO); disable_pci: pci_disable_device(pdev); return ret; }1.2、初始化qxl设备时通过setup_slot函数注册内存显示区域。int qxl_device_init(struct qxl_device *qdev, struct pci_dev *pdev) { int r, sb; qdev-ddev.pdev pdev; pci_set_drvdata(pdev, qdev-ddev); mutex_init(qdev-gem.mutex); mutex_init(qdev-update_area_mutex); mutex_init(qdev-release_mutex); mutex_init(qdev-surf_evict_mutex); qxl_gem_init(qdev); qdev-rom_base pci_resource_start(pdev, 2); qdev-rom_size pci_resource_len(pdev, 2); qdev-vram_base pci_resource_start(pdev, 0); qdev-io_base pci_resource_start(pdev, 3); qdev-vram_mapping io_mapping_create_wc(qdev-vram_base, pci_resource_len(pdev, 0)); if (!qdev-vram_mapping) { pr_err(Unable to create vram_mapping); return -ENOMEM; } if (pci_resource_len(pdev, 4) 0) { /* 64bit surface bar present */ sb 4; qdev-surfaceram_base pci_resource_start(pdev, sb); qdev-surfaceram_size pci_resource_len(pdev, sb); qdev-surface_mapping io_mapping_create_wc(qdev-surfaceram_base, qdev-surfaceram_size); } if (qdev-surface_mapping NULL) { /* 64bit surface bar not present (or mapping failed) */ sb 1; qdev-surfaceram_base pci_resource_start(pdev, sb); qdev-surfaceram_size pci_resource_len(pdev, sb); qdev-surface_mapping io_mapping_create_wc(qdev-surfaceram_base, qdev-surfaceram_size); if (!qdev-surface_mapping) { pr_err(Unable to create surface_mapping); r -ENOMEM; goto vram_mapping_free; } } DRM_DEBUG_KMS(qxl: vram %llx-%llx(%dM %dk), surface %llx-%llx(%dM %dk, %s)\n, (unsigned long long)qdev-vram_base, (unsigned long long)pci_resource_end(pdev, 0), (int)pci_resource_len(pdev, 0) / 1024 / 1024, (int)pci_resource_len(pdev, 0) / 1024, (unsigned long long)qdev-surfaceram_base, (unsigned long long)pci_resource_end(pdev, sb), (int)qdev-surfaceram_size / 1024 / 1024, (int)qdev-surfaceram_size / 1024, (sb 4) ? 64bit : 32bit); qdev-rom ioremap(qdev-rom_base, qdev-rom_size); if (!qdev-rom) { pr_err(Unable to ioremap ROM\n); r -ENOMEM; goto surface_mapping_free; } if (!qxl_check_device(qdev)) { r -ENODEV; goto rom_unmap; } r qxl_bo_init(qdev); if (r) { DRM_ERROR(bo init failed %d\n, r); goto rom_unmap; } int header_offset qdev-rom-ram_header_offset; int vram_size pci_resource_len(pdev, 0); qdev-ram_header ioremap(qdev-vram_base header_offset, sizeof(*qdev-ram_header)); if (!qdev-ram_header) { DRM_ERROR(Unable to ioremap RAM header\n); r -ENOMEM; goto bo_fini; } qdev-command_ring qxl_ring_create((qdev-ram_header-cmd_ring_hdr), sizeof(struct qxl_command), QXL_COMMAND_RING_SIZE, qdev-io_base QXL_IO_NOTIFY_CMD, false, qdev-display_event); if (!qdev-command_ring) { DRM_ERROR(Unable to create command ring\n); r -ENOMEM; goto ram_header_unmap; } qdev-cursor_ring qxl_ring_create( (qdev-ram_header-cursor_ring_hdr), sizeof(struct qxl_command), QXL_CURSOR_RING_SIZE, qdev-io_base QXL_IO_NOTIFY_CURSOR, false, qdev-cursor_event); if (!qdev-cursor_ring) { DRM_ERROR(Unable to create cursor ring\n); r -ENOMEM; goto command_ring_free; } qdev-release_ring qxl_ring_create( (qdev-ram_header-release_ring_hdr), sizeof(uint64_t), QXL_RELEASE_RING_SIZE, 0, true, NULL); if (!qdev-release_ring) { DRM_ERROR(Unable to create release ring\n); r -ENOMEM; goto cursor_ring_free; } idr_init(qdev-release_idr); spin_lock_init(qdev-release_idr_lock); spin_lock_init(qdev-release_lock); idr_init(qdev-surf_id_idr); spin_lock_init(qdev-surf_id_idr_lock); mutex_init(qdev-async_io_mutex); /* reset the device into a known state - no memslots, no primary * created, no surfaces. */ qxl_io_reset(qdev); /* must initialize irq before first async io - slot creation */ r qxl_irq_init(qdev); if (r) { DRM_ERROR(Unable to init qxl irq\n); goto release_ring_free; } /* * Note that virtual is surface0. We rely on the single ioremap done * before. */ setup_slot(qdev, qdev-main_slot, 0, main, (unsigned long)qdev-vram_base, (unsigned long)qdev-rom-ram_header_offset); setup_slot(qdev, qdev-surfaces_slot, 1, surfaces, (unsigned long)qdev-surfaceram_base, (unsigned long)qdev-surfaceram_size); INIT_WORK(qdev-gc_work, qxl_gc_work); return 0; release_ring_free: qxl_ring_free(qdev-release_ring); cursor_ring_free: qxl_ring_free(qdev-cursor_ring); command_ring_free: qxl_ring_free(qdev-command_ring); ram_header_unmap: iounmap(qdev-ram_header); bo_fini: qxl_bo_fini(qdev); rom_unmap: iounmap(qdev-rom); surface_mapping_free: io_mapping_free(qdev-surface_mapping); vram_mapping_free: io_mapping_free(qdev-vram_mapping); return r; }1.3、驱动最终调用qxl_io_memslot_add函数将QXL_IO_MEMSLOT_ADD_ASYNC命令给到qxl设备。void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id) { DRM_DEBUG_DRIVER(qxl_memslot_add %d\n, id); wait_for_io_cmd(qdev, id, QXL_IO_MEMSLOT_ADD_ASYNC); } static void setup_hw_slot(struct qxl_device *qdev, struct qxl_memslot *slot) { if (!qdev-ram_header || !qdev-rom) { DRM_ERROR(qxl: null ram_header or rom\n); return; } qdev-ram_header-mem_slot.mem_start slot-start_phys_addr; qdev-ram_header-mem_slot.mem_end slot-start_phys_addr slot-size; qxl_io_memslot_add(qdev, qdev-rom-slots_start slot-index); } static void setup_slot(struct qxl_device *qdev, struct qxl_memslot *slot, unsigned int slot_index, const char *slot_name, unsigned long start_phys_addr, unsigned long size) { uint64_t high_bits; slot-index slot_index; slot-name slot_name; slot-start_phys_addr start_phys_addr; slot-size size; setup_hw_slot(qdev, slot); slot-generation qdev-rom-slot_generation; high_bits (qdev-rom-slots_start slot-index) qdev-rom-slot_gen_bits; high_bits | slot-generation; high_bits (64 - (qdev-rom-slot_gen_bits qdev-rom-slot_id_bits)); slot-high_bits high_bits; DRM_INFO(slot %d (%s): base 0x%08lx, size 0x%08lx\n, slot-index, slot-name, (unsigned long)slot-start_phys_addr, (unsigned long)slot-size); }2、qxl后端设备2.1、接收到ioport指令QXL_IO_MEMSLOT_ADDstatic void ioport_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { PCIQXLDevice *d opaque; uint32_t io_port addr; qxl_async_io async QXL_SYNC; uint32_t orig_io_port; if (d-guest_bug io_port ! QXL_IO_RESET) { return; } if (d-revision QXL_REVISION_STABLE_V10 io_port QXL_IO_FLUSH_RELEASE) { qxl_set_guest_bug(d, unsupported io %d for revision %d\n, io_port, d-revision); return; } switch (io_port) { case QXL_IO_RESET: case QXL_IO_SET_MODE: case QXL_IO_MEMSLOT_ADD: case QXL_IO_MEMSLOT_DEL: case QXL_IO_CREATE_PRIMARY: case QXL_IO_UPDATE_IRQ: case QXL_IO_LOG: case QXL_IO_MEMSLOT_ADD_ASYNC: case QXL_IO_CREATE_PRIMARY_ASYNC: break; default: if (d-mode ! QXL_MODE_VGA) { break; } trace_qxl_io_unexpected_vga_mode(d-id, addr, val, io_port_to_string(io_port)); /* be nice to buggy guest drivers */ if (io_port QXL_IO_UPDATE_AREA_ASYNC io_port QXL_IO_RANGE_SIZE) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); } return; } /* we change the io_port to avoid ifdeffery in the main switch */ orig_io_port io_port; switch (io_port) { case QXL_IO_UPDATE_AREA_ASYNC: io_port QXL_IO_UPDATE_AREA; goto async_common; case QXL_IO_MEMSLOT_ADD_ASYNC: io_port QXL_IO_MEMSLOT_ADD; goto async_common; case QXL_IO_CREATE_PRIMARY_ASYNC: io_port QXL_IO_CREATE_PRIMARY; goto async_common; case QXL_IO_DESTROY_PRIMARY_ASYNC: io_port QXL_IO_DESTROY_PRIMARY; goto async_common; case QXL_IO_DESTROY_SURFACE_ASYNC: io_port QXL_IO_DESTROY_SURFACE_WAIT; goto async_common; case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: io_port QXL_IO_DESTROY_ALL_SURFACES; goto async_common; case QXL_IO_FLUSH_SURFACES_ASYNC: case QXL_IO_MONITORS_CONFIG_ASYNC: async_common: async QXL_ASYNC; WITH_QEMU_LOCK_GUARD(d-async_lock) { if (d-current_async ! QXL_UNDEFINED_IO) { qxl_set_guest_bug(d, %d async started before last (%d) complete, io_port, d-current_async); return; } d-current_async orig_io_port; } break; default: break; } trace_qxl_io_write(d-id, qxl_mode_to_string(d-mode), addr, io_port_to_string(addr), val, size, async); switch (io_port) { case QXL_IO_UPDATE_AREA: { QXLCookie *cookie NULL; QXLRect update d-ram-update_area; if (d-ram-update_surface d-ssd.num_surfaces) { qxl_set_guest_bug(d, QXL_IO_UPDATE_AREA: invalid surface id %d\n, d-ram-update_surface); break; } if (update.left update.right || update.top update.bottom || update.left 0 || update.top 0) { qxl_set_guest_bug(d, QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n, update.left, update.top, update.right, update.bottom); if (update.left update.right || update.top update.bottom) { /* old drivers may provide empty area, keep going */ qxl_clear_guest_bug(d); goto cancel_async; } break; } if (async QXL_ASYNC) { cookie qxl_cookie_new(QXL_COOKIE_TYPE_IO, QXL_IO_UPDATE_AREA_ASYNC); cookie-u.area update; } qxl_spice_update_area(d, d-ram-update_surface, cookie ? cookie-u.area : update, NULL, 0, 0, async, cookie); break; } case QXL_IO_NOTIFY_CMD: qemu_spice_wakeup(d-ssd); break; case QXL_IO_NOTIFY_CURSOR: qemu_spice_wakeup(d-ssd); break; case QXL_IO_UPDATE_IRQ: qxl_update_irq(d); break; case QXL_IO_NOTIFY_OOM: if (!SPICE_RING_IS_EMPTY(d-ram-release_ring)) { break; } d-oom_running 1; qxl_spice_oom(d); d-oom_running 0; break; case QXL_IO_SET_MODE: qxl_set_mode(d, val, 0); break; case QXL_IO_LOG: #ifdef CONFIG_MODULES /* * FIXME * trace_event_get_state_backends() does not work for modules, * it leads to undefined symbol: qemu_qxl_io_log_semaphore */ if (true) { #else if (trace_event_get_state_backends(TRACE_QXL_IO_LOG) || d-guestdebug) { #endif /* We cannot trust the guest to NUL terminate d-ram-log_buf */ char *log_buf g_strndup((const char *)d-ram-log_buf, sizeof(d-ram-log_buf)); trace_qxl_io_log(d-id, log_buf); if (d-guestdebug) { fprintf(stderr, qxl/guest-%d: % PRId64 : %s, d-id, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), log_buf); } g_free(log_buf); } break; case QXL_IO_RESET: qxl_hard_reset(d, 0); break; case QXL_IO_MEMSLOT_ADD: if (val NUM_MEMSLOTS) { qxl_set_guest_bug(d, QXL_IO_MEMSLOT_ADD: val out of range); break; } if (d-guest_slots[val].active) { qxl_set_guest_bug(d, QXL_IO_MEMSLOT_ADD: memory slot already active); break; } d-guest_slots[val].slot d-ram-mem_slot; qxl_add_memslot(d, val, 0, async); break; case QXL_IO_MEMSLOT_DEL: if (val NUM_MEMSLOTS) { qxl_set_guest_bug(d, QXL_IO_MEMSLOT_DEL: val out of range); break; } qxl_del_memslot(d, val); break; case QXL_IO_CREATE_PRIMARY: if (val ! 0) { qxl_set_guest_bug(d, QXL_IO_CREATE_PRIMARY (async%d): val ! 0, async); goto cancel_async; } d-guest_primary.surface d-ram-create_surface; qxl_create_guest_primary(d, 0, async); break; case QXL_IO_DESTROY_PRIMARY: if (val ! 0) { qxl_set_guest_bug(d, QXL_IO_DESTROY_PRIMARY (async%d): val ! 0, async); goto cancel_async; } if (!qxl_destroy_primary(d, async)) { trace_qxl_io_destroy_primary_ignored(d-id, qxl_mode_to_string(d-mode)); goto cancel_async; } break; case QXL_IO_DESTROY_SURFACE_WAIT: if (val d-ssd.num_surfaces) { qxl_set_guest_bug(d, QXL_IO_DESTROY_SURFACE (async%d): % PRIu64 NUM_SURFACES, async, val); goto cancel_async; } qxl_spice_destroy_surface_wait(d, val, async); break; case QXL_IO_FLUSH_RELEASE: { QXLReleaseRing *ring d-ram-release_ring; if (ring-prod - ring-cons 1 ring-num_items) { fprintf(stderr, ERROR: no flush, full release ring [p%d,%dc]\n, ring-prod, ring-cons); } qxl_push_free_res(d, 1 /* flush */); break; } case QXL_IO_FLUSH_SURFACES_ASYNC: qxl_spice_flush_surfaces_async(d); break; case QXL_IO_DESTROY_ALL_SURFACES: d-mode QXL_MODE_UNDEFINED; qxl_spice_destroy_surfaces(d, async); break; case QXL_IO_MONITORS_CONFIG_ASYNC: qxl_spice_monitors_config_async(d, 0); break; default: qxl_set_guest_bug(d, %s: unexpected ioport0x%x\n, __func__, io_port); } return; cancel_async: if (async) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); qemu_mutex_lock(d-async_lock); d-current_async QXL_UNDEFINED_IO; qemu_mutex_unlock(d-async_lock); } }2.2、qxl_add_memslot将guest os物理地址转换成qemu进程的虚拟地址并通过QemuConsole发送给spice。static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, qxl_async_io async) { static const int regions[] { QXL_RAM_RANGE_INDEX, QXL_VRAM_RANGE_INDEX, QXL_VRAM64_RANGE_INDEX, }; uint64_t guest_start; uint64_t guest_end; int pci_region; pcibus_t pci_start; pcibus_t pci_end; MemoryRegion *mr; intptr_t virt_start; QXLDevMemSlot memslot; int i; guest_start le64_to_cpu(d-guest_slots[slot_id].slot.mem_start); guest_end le64_to_cpu(d-guest_slots[slot_id].slot.mem_end); trace_qxl_memslot_add_guest(d-id, slot_id, guest_start, guest_end); if (slot_id NUM_MEMSLOTS) { qxl_set_guest_bug(d, %s: slot_id NUM_MEMSLOTS %d %d, __func__, slot_id, NUM_MEMSLOTS); return 1; } if (guest_start guest_end) { qxl_set_guest_bug(d, %s: guest_start guest_end 0x% PRIx64 0x% PRIx64, __func__, guest_start, guest_end); return 1; } for (i 0; i ARRAY_SIZE(regions); i) { pci_region regions[i]; pci_start d-pci.io_regions[pci_region].addr; pci_end pci_start d-pci.io_regions[pci_region].size; /* mapped? */ if (pci_start -1) { continue; } /* start address in range ? */ if (guest_start pci_start || guest_start pci_end) { continue; } /* end address in range ? */ if (guest_end pci_end) { continue; } /* passed */ break; } if (i ARRAY_SIZE(regions)) { qxl_set_guest_bug(d, %s: finished loop without match, __func__); return 1; } switch (pci_region) { case QXL_RAM_RANGE_INDEX: mr d-vga.vram; break; case QXL_VRAM_RANGE_INDEX: case 4 /* vram 64bit */: mr d-vram_bar; break; default: /* should not happen */ qxl_set_guest_bug(d, %s: pci_region %d, __func__, pci_region); return 1; } assert(guest_end - pci_start memory_region_size(mr)); virt_start (intptr_t)memory_region_get_ram_ptr(mr); memslot.slot_id slot_id; memslot.slot_group_id MEMSLOT_GROUP_GUEST; /* guest group */ memslot.virt_start virt_start (guest_start - pci_start); memslot.virt_end virt_start (guest_end - pci_start); memslot.addr_delta memslot.virt_start - delta; memslot.generation d-rom-slot_generation 0; qxl_rom_set_dirty(d); qemu_spice_add_memslot(d-ssd, memslot, async); d-guest_slots[slot_id].mr mr; d-guest_slots[slot_id].offset memslot.virt_start - virt_start; d-guest_slots[slot_id].size memslot.virt_end - memslot.virt_start; d-guest_slots[slot_id].delta delta; d-guest_slots[slot_id].active 1; return 0; }3、libspice协议端3.1、获取qxl设备的调度器Dispatcher *red_qxl_get_dispatcher(QXLInstance *qxl) { return qxl-st-dispatcher.get(); }3.2、设置调度器处理回调函数来处理收到的RedWorkerMessageAddMemslot消息static void register_callbacks(Dispatcher *dispatcher) { /* TODO: register cursor display specific msg in respective channel files */ register_handler(dispatcher, handle_dev_update, true); register_handler(dispatcher, handle_dev_update_async, false); register_handler(dispatcher, handle_dev_add_memslot, true); register_handler(dispatcher, handle_dev_add_memslot_async, false); register_handler(dispatcher, handle_dev_del_memslot, false); register_handler(dispatcher, handle_dev_destroy_surfaces, true); register_handler(dispatcher, handle_dev_destroy_surfaces_async, false); register_handler(dispatcher, handle_dev_destroy_primary_surface, true); register_handler(dispatcher, handle_dev_destroy_primary_surface_async, false); register_handler(dispatcher, handle_dev_create_primary_surface_async, false); register_handler(dispatcher, handle_dev_create_primary_surface, true); register_handler(dispatcher, handle_dev_reset_image_cache, true); register_handler(dispatcher, handle_dev_reset_cursor, true); register_handler(dispatcher, handle_dev_wakeup, false); register_handler(dispatcher, handle_dev_oom, false); register_handler(dispatcher, handle_dev_start, false); register_handler(dispatcher, handle_dev_flush_surfaces_async, false); register_handler(dispatcher, handle_dev_stop, true); register_handler(dispatcher, handle_dev_loadvm_commands, true); register_handler(dispatcher, handle_dev_set_compression, false); register_handler(dispatcher, handle_dev_set_streaming_video, false); register_handler(dispatcher, handle_dev_set_video_codecs, false); register_handler(dispatcher, handle_dev_set_mouse_mode, false); register_handler(dispatcher, handle_dev_destroy_surface_wait, true); register_handler(dispatcher, handle_dev_destroy_surface_wait_async, false); register_handler(dispatcher, handle_dev_reset_memslots, false); register_handler(dispatcher, handle_dev_monitors_config_async, false); register_handler(dispatcher, handle_dev_driver_unload, false); register_handler(dispatcher, handle_dev_gl_scanout, false); register_handler(dispatcher, handle_dev_gl_draw_async, false); register_handler(dispatcher, handle_dev_close, false); }3.3、spice新增内存区域static void handle_dev_add_memslot(RedWorker* worker, RedWorkerMessageAddMemslot* msg) { QXLDevMemSlot mem_slot msg-mem_slot; memslot_info_add_slot(worker-mem_slots, mem_slot.slot_group_id, mem_slot.slot_id, mem_slot.addr_delta, mem_slot.virt_start, mem_slot.virt_end, mem_slot.generation); }