Files
VBoxGuest/core/vmmdev.inc
Alexey Mikhailov 0f400bc0e0 #1 init в репу
2026-01-06 15:43:37 +03:00

294 lines
8.2 KiB
HTML

; =============================================================================
; 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 \
<sizeof.VMMDEV_GUEST_INFO, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_REPORT_GUEST_INFO, \
0, 0, 0>, \
VMMDEV_VERSION, \
VBOXOSTYPE_KOLIBRIOS
vmmdev_guest_caps VMMDEV_GUEST_CAPS \
<sizeof.VMMDEV_GUEST_CAPS, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_SET_GUEST_CAPS, \
0, 0, 0>, \
VBOXGUEST_OS_SUPPORTS_CAPS
vmmdev_ack VMMDEV_ACK_EVENTS \
<sizeof.VMMDEV_ACK_EVENTS, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_ACKNOWLEDGE_EVENTS, \
0, 0, 0>, \
0
vmmdev_display VMMDEV_DISPLAY_CHANGE \
<sizeof.VMMDEV_DISPLAY_CHANGE, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_GET_DISPLAY_CHANGE, \
0, 0, 0>, \
0, 0, 0, 1