294 lines
8.2 KiB
HTML
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
|