; ============================================================================= ; VMMDev Low-level Protocol Implementation ; ============================================================================= ; Send VMMDev request via I/O port proc vmmdev_request_perform uses ebx ecx edx, phys_addr:dword DEBUGF 2, "vmmdev_request_perform\n" mov eax, [phys_addr] DEBUGF 2, "[vmmdev] Sending request at phys=0x%x\n", eax vbox_send_pack ret endp ; Initialize VMMDev device proc vmmdev_init uses ebx esi edi, pci_dev:dword DEBUGF 2, "vmmdev_init" mov ebx, [pci_dev] ; Get KolibriOS display structure invoke GetDisplay mov [kos_display_ptr], eax ; Read and attach IRQ invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.interrupt_line movzx eax, al DEBUGF 2, "[vmmdev] IRQ line: %d\n", eax stdcall vbox_install_irq_handler, eax jnz .irq_failed ; Read BAR0 (I/O port) - FIX: use full 32-bit mask invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.base_addr_0 and eax, not 0xF mov [vbox_device.port], ax DEBUGF 2, "[vmmdev] I/O Port: 0x%x (%d)\n", eax, eax ; Read BAR1 (MMIO) - FIX: use full 32-bit mask invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.base_addr_1 and eax, not 0xF mov [vbox_device.mmio_phys], eax DEBUGF 2, "[vmmdev] MMIO physical: 0x%x\n", eax ; Map MMIO region invoke MapIoMem, eax, VMMDEV_MEM_SIZE, PG_NOCACHE + PG_SW test eax, eax jz .mmio_failed mov [vbox_device.mmio], eax DEBUGF 2, "[vmmdev] MMIO virtual: 0x%x\n", eax ; Check VMMDev version mov edi, eax mov eax, [edi + VMMDEV_MEMORY.version] DEBUGF 2, "[vmmdev] VMMDev version: 0x%x\n", eax xor eax, eax ret .irq_failed: DEBUGF 1, "[VBoxGuest] Failed to attach IRQ handler\n" mov eax, VERR_GENERAL_FAILURE ret .mmio_failed: DEBUGF 1, "[VBoxGuest] Failed to map MMIO region\n" mov eax, VERR_NO_MEMORY ret endp ; Send guest info to host proc vmmdev_send_guest_info DEBUGF 2, "vmmdev_send_guest_info" DEBUGF 2, "[vmmdev] Sending guest info...\n" mov eax, vmmdev_guest_info invoke GetPhysAddr stdcall vmmdev_request_perform, eax mov eax, [vmmdev_guest_info.header.rc] CHECK_VMMDEV_RC .error DEBUGF 2, "[vmmdev] Guest info sent successfully\n" xor eax, eax ret .error: push eax stdcall error_get_message, eax mov esi, eax pop eax DEBUGF 2, "[vmmdev] send_guest_info failed: %s (rc=0x%x)\n", esi, eax ret endp ; Set guest capabilities proc vmmdev_set_guest_caps DEBUGF 2, "[vmmdev] Setting guest capabilities...\n" mov eax, vmmdev_guest_caps invoke GetPhysAddr stdcall vmmdev_request_perform, eax mov eax, [vmmdev_guest_caps.header.rc] CHECK_VMMDEV_RC .error DEBUGF 2, "[vmmdev] Capabilities set\n" xor eax, eax ret .error: push eax stdcall error_get_message, eax mov esi, eax pop eax DEBUGF 2, "[vmmdev] set_guest_caps failed: %s (rc=0x%x)\n", esi, eax ret endp ; ============================================================================= ; ПРОЦЕДУРА: vmmdev_ack_events ; Подтверждение событий в MMIO ; Вход: EAX = флаги событий для подтверждения ; ============================================================================= proc vmmdev_ack_events uses ebx, events:dword mov eax, [vbox_device.mmio] test eax, eax jz .no_mmio ; Записываем флаги обратно для ACK mov ebx, [events] mov [eax + VMMDEV_MEMORY.event_ack], ebx .no_mmio: ret endp ; ============================================================================= ; ПРОЦЕДУРА: vmmdev_enable_events ; Включение генерации событий ; Вход: EAX = маска событий для включения ; ============================================================================= proc vmmdev_enable_events uses ebx, mask:dword mov eax, [vbox_device.mmio] test eax, eax jz .no_mmio ; Читаем текущие enabled события mov ebx, [eax + VMMDEV_MEMORY.event_enabled] ; Добавляем новые события or ebx, [mask] ; Записываем обратно mov [eax + VMMDEV_MEMORY.event_enabled], ebx DEBUGF 2, "[vmmdev] Enabled events: 0x%x (total: 0x%x)\n", [mask], ebx .no_mmio: ret endp ; ============================================================================= ; ПРОЦЕДУРА: vmmdev_disable_events ; Отключение генерации событий ; Вход: EAX = маска событий для отключения ; ============================================================================= proc vmmdev_disable_events uses ebx, mask:dword mov eax, [vbox_device.mmio] test eax, eax jz .no_mmio ; Читаем текущие enabled события mov ebx, [eax + VMMDEV_MEMORY.event_enabled] ; Убираем указанные события mov ecx, [mask] not ecx and ebx, ecx ; Записываем обратно mov [eax + VMMDEV_MEMORY.event_enabled], ebx DEBUGF 2, "[vmmdev] Disabled events: 0x%x (total: 0x%x)\n", [mask], ebx .no_mmio: ret endp ; ============================================================================= ; ПРОЦЕДУРА: vmmdev_get_pending_events ; Получение pending событий (без ACK) ; Возврат: EAX = флаги pending событий ; ============================================================================= proc vmmdev_get_pending_events mov eax, [vbox_device.mmio] test eax, eax jz .no_events mov eax, [eax + VMMDEV_MEMORY.event_flags] ret .no_events: xor eax, eax ret endp ; Enable interrupts proc vmmdev_enable_interrupts DEBUGF 2, "vmmdev_enable_interrupts\n" ; 1. Получаем указатель на MMIO mov ebx, [vbox_device.mmio] test ebx, ebx jz .no_mmio ; 2. ACK все pending события mov eax, [ebx + VMMDEV_MEMORY.event_flags] test eax, eax jz .no_pending_events ; Подтверждаем через MMIO mov [ebx + VMMDEV_MEMORY.event_ack], eax ; И через VMMDev запрос (чтобы хост знал) push eax mov eax, vmmdev_ack mov [eax + VMMDEV_ACK_EVENTS.events], eax invoke GetPhysAddr stdcall vmmdev_request_perform, eax pop eax DEBUGF 2, "[vmmdev] ACKed pending events: 0x%x\n", eax .no_pending_events: ; 3. Включаем генерацию событий которые нас интересуют mov eax, VMMDEV_EVENT_DISPLAY_CHANGE or VMMDEV_EVENT_HGCM or VMMDEV_EVENT_MOUSE_POSITION_CHANGED stdcall vmmdev_enable_events, eax ; 4. Проверяем что события включились mov eax, [ebx + VMMDEV_MEMORY.event_enabled] DEBUGF 2, "[vmmdev] Event enabled mask: 0x%x\n", eax ; 5. Также сообщаем хосту что мы готовы получать события ; через запрос VMMDEV_REQ_ACKNOWLEDGE_EVENTS mov eax, vmmdev_ack mov dword [eax + VMMDEV_ACK_EVENTS.events], 0xFFFFFFFF ; Подписываемся на все invoke GetPhysAddr stdcall vmmdev_request_perform, eax DEBUGF 2, "[vmmdev] Interrupts enabled\n" ret .no_mmio: DEBUGF 1, "[vmmdev] ERROR: No MMIO for enable_interrupts\n" ret endp ; Prepared VMMDev packets align 4 vmmdev_guest_info VMMDEV_GUEST_INFO \ , \ VMMDEV_VERSION, \ VBOXOSTYPE_KOLIBRIOS vmmdev_guest_caps VMMDEV_GUEST_CAPS \ , \ VBOXGUEST_OS_SUPPORTS_CAPS vmmdev_ack VMMDEV_ACK_EVENTS \ , \ 0 vmmdev_display VMMDEV_DISPLAY_CHANGE \ , \ 0, 0, 0, 1