458 lines
15 KiB
HTML
458 lines
15 KiB
HTML
; =============================================================================
|
||
; VirtualBox IRQ Handler
|
||
; =============================================================================
|
||
|
||
VMMDEV_MMIO_EVENTS = 0x00
|
||
VMMDEV_MMIO_EVENT_CLEAR = 0x04
|
||
|
||
; Глобальные переменные для IRQ обработки
|
||
align 4
|
||
vbox_pending_events dd 0
|
||
vbox_service_running dd 1 ; 1 = running, 0 = should exit
|
||
vbox_service_event dd 0
|
||
vbox_service_event_code dd 0
|
||
|
||
vbox_service_thread_handle dd ?
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_install_irq_handler
|
||
; Установка IRQ обработчика при инициализации драйвера
|
||
; =============================================================================
|
||
proc vbox_install_irq_handler stdcall, irq_line
|
||
DEBUGF 1, "[irq] Installing IRQ handler for line %d\n", [irq_line]
|
||
|
||
; Устанавливаем обработчик прерывания
|
||
invoke AttachIntHandler, [irq_line], vbox_irq_handler, 0
|
||
test eax, eax
|
||
jz .error
|
||
|
||
DEBUGF 1, "[irq] IRQ handler installed successfully\n"
|
||
xor eax, eax
|
||
ret
|
||
|
||
.error:
|
||
DEBUGF 2, "[irq] ERROR: Failed to install IRQ handler, eax=0x%x\n", eax
|
||
mov eax, VERR_INTERNAL_ERROR
|
||
ret
|
||
endp
|
||
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_irq_handler
|
||
; Top half IRQ обработчика - минимум работы в IRQ контексте
|
||
; =============================================================================
|
||
proc vbox_irq_handler
|
||
push ebx esi edi
|
||
|
||
; 1. Проверяем, наш ли это IRQ
|
||
mov eax, [vbox_device.mmio]
|
||
test eax, eax
|
||
jz .not_ours
|
||
|
||
; 2. Читаем флаги событий из MMIO
|
||
mov eax, [eax + VMMDEV_MEMORY.event_flags]
|
||
test eax, eax
|
||
jz .not_ours
|
||
|
||
DEBUGF 2, "[irq] IRQ received, flags=0x%x\n", eax
|
||
|
||
mov ebx, eax
|
||
|
||
; 3. Фильтруем интересующие нас события
|
||
and eax, VMMDEV_EVENT_DISPLAY_CHANGE or VMMDEV_EVENT_HGCM or VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
||
; test eax, eax
|
||
; jz .no_relevant_events
|
||
|
||
; 4. Атомарно добавляем события в pending
|
||
lock or [vbox_pending_events], eax
|
||
|
||
; 5. ACK события (безопасно для IRQ контекста)
|
||
; push eax
|
||
; stdcall vmmdev_ack_events, eax
|
||
; pop eax
|
||
|
||
mov eax, [vbox_device.mmio]
|
||
mov [eax + VMMDEV_MEMORY.event_ack], ebx
|
||
; mov [edx + 0x18], ebx ; ACK ALL EVENTS
|
||
|
||
|
||
DEBUGF 2, "[irq] Acknowledged events: 0x%x\n", eax
|
||
|
||
; ; 6. Если есть HGCM событие (clipboard), будим service thread
|
||
; test eax, VMMDEV_EVENT_HGCM
|
||
; jz .no_hgcm_wakeup
|
||
|
||
; ; Проверяем, создан ли service thread
|
||
; cmp dword [vbox_service_event], 0
|
||
; je .no_hgcm_wakeup
|
||
|
||
; ; Будим service thread через event
|
||
; mov eax, [vbox_service_event]
|
||
; mov ebx, [vbox_service_event_code]
|
||
; invoke RaiseEvent
|
||
|
||
DEBUGF 2, "[irq] Woke up service thread for HGCM\n"
|
||
|
||
; .no_hgcm_wakeup:
|
||
|
||
; ; 7. Для display change можно обработать быстрее в IRQ
|
||
; ; Быстрая обработка display change в IRQ контексте
|
||
; test eax, VMMDEV_EVENT_DISPLAY_CHANGE
|
||
; jz .no_display_irq
|
||
|
||
; ; Display change требует немедленной реакции
|
||
; ; Устанавливаем флаг для быстрой обработки
|
||
; DEBUGF 2, "[irq] Display change detected, scheduling immediate update\n"
|
||
|
||
; ; Можно также сразу обработать display change,
|
||
; ; если операция быстрая и безопасная в IRQ контексте
|
||
; push eax
|
||
; call display_change_irq ; быстрая версия display_change
|
||
; pop eax
|
||
|
||
; .no_display_irq:
|
||
|
||
; ; 8. Если есть mouse events, также можно обработать быстро
|
||
; test eax, VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
||
; jz .no_mouse_irq
|
||
|
||
; ; Быстрая обработка мыши в IRQ контексте
|
||
; DEBUGF 2, "[irq] Mouse position changed\n"
|
||
|
||
; ; Здесь можно обновить кэшированные координаты мыши
|
||
; ; или установить флаг для обработки в service thread
|
||
|
||
; .no_mouse_irq:
|
||
|
||
; pop edi esi ebx
|
||
; mov eax, 1 ; IRQ обработан
|
||
; ret
|
||
|
||
; .no_relevant_events:
|
||
; DEBUGF 2, "[irq] No relevant events (filtered)\n"
|
||
|
||
; ; Все равно ACK события, чтобы не залипали
|
||
; stdcall vmmdev_ack_events, ebx
|
||
|
||
pop edi esi ebx
|
||
mov eax, 1 ; IRQ обработан
|
||
ret
|
||
|
||
.not_ours:
|
||
pop edi esi ebx
|
||
xor eax, eax ; не наш IRQ
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: display_change_irq
|
||
; Быстрая обработка изменения дисплея в IRQ контексте
|
||
; =============================================================================
|
||
proc display_change_irq
|
||
; Минимальная обработка в IRQ контексте
|
||
; Основная обработка будет в service thread
|
||
|
||
; Просто устанавливаем флаг для полной обработки
|
||
; или сразу читаем новое разрешение
|
||
|
||
push eax ebx
|
||
|
||
; Быстрое чтение нового разрешения из MMIO
|
||
mov eax, [vbox_device.mmio]
|
||
test eax, eax
|
||
jz .no_mmio
|
||
|
||
; Здесь можно прочитать новые параметры дисплея
|
||
; но основную работу оставляем для service thread
|
||
|
||
.no_mmio:
|
||
pop ebx eax
|
||
ret
|
||
endp
|
||
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_uninstall_irq_handler
|
||
; Удаление IRQ обработчика при выгрузке драйвера
|
||
; =============================================================================
|
||
proc vbox_uninstall_irq_handler
|
||
DEBUGF 2, "[irq] Uninstalling IRQ handler\n"
|
||
|
||
; В KolibriOS нет стандартного способа удалить обработчик
|
||
; Просто отмечаем, что обработчик больше не активен
|
||
|
||
mov dword [vbox_service_running], 0
|
||
|
||
xor eax, eax
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_service_init
|
||
; Инициализация service thread
|
||
; =============================================================================
|
||
proc vbox_service_init
|
||
DEBUGF 2, "[service] Initializing service thread\n"
|
||
|
||
; 1. Создаём event для синхронизации
|
||
xor esi, esi
|
||
xor ecx, ecx
|
||
invoke CreateEvent
|
||
test eax, eax
|
||
jz .event_create_failed
|
||
|
||
mov [vbox_service_event], eax
|
||
mov [vbox_service_event_code], edx
|
||
|
||
DEBUGF 2, "[service] Event created: handle=0x%x, code=0x%x\n", eax, edx
|
||
|
||
; 2. Создаём service thread
|
||
push ebx ecx edx esi edi
|
||
movi ebx, 1 ; priority = 1 (низкий)
|
||
mov ecx, vbox_service_thread ; entry point
|
||
xor edx, edx ; parameter = NULL
|
||
invoke CreateThread
|
||
pop edi esi edx ecx ebx
|
||
|
||
cmp eax, -1
|
||
je .thread_create_failed
|
||
|
||
mov [vbox_service_thread_handle], eax
|
||
DEBUGF 2, "[service] Service thread created, handle=0x%x\n", eax
|
||
|
||
; 3. Устанавливаем флаг запуска
|
||
mov dword [vbox_service_running], 1
|
||
mov dword [vbox_pending_events], 0
|
||
|
||
xor eax, eax
|
||
ret
|
||
|
||
.event_create_failed:
|
||
DEBUGF 1, "[service] ERROR: Failed to create event\n"
|
||
or eax, -1
|
||
ret
|
||
|
||
.thread_create_failed:
|
||
DEBUGF 1, "[service] ERROR: Failed to create service thread\n"
|
||
|
||
; Освобождаем event
|
||
mov eax, [vbox_service_event]
|
||
test eax, eax
|
||
jz @f
|
||
invoke DestroyEvent
|
||
@@:
|
||
mov dword [vbox_service_event], 0
|
||
or eax, -1
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_service_cleanup
|
||
; Очистка ресурсов service thread
|
||
; =============================================================================
|
||
proc vbox_service_cleanup
|
||
DEBUGF 2, "[service] Cleaning up service thread\n"
|
||
|
||
; 1. Устанавливаем флаг завершения
|
||
mov dword [vbox_service_running], 0
|
||
|
||
; 2. Будим thread для завершения (если спит)
|
||
mov eax, [vbox_service_event]
|
||
test eax, eax
|
||
jz .no_event
|
||
mov ebx, [vbox_service_event_code]
|
||
invoke RaiseEvent
|
||
|
||
; 3. Ждем завершения thread (краткий таймаут)
|
||
push esi
|
||
movi esi, 50 ; 50ms timeout
|
||
invoke Sleep
|
||
pop esi
|
||
|
||
.no_event:
|
||
; 4. Освобождаем event
|
||
mov eax, [vbox_service_event]
|
||
test eax, eax
|
||
jz @f
|
||
invoke DestroyEvent
|
||
@@:
|
||
mov dword [vbox_service_event], 0
|
||
mov dword [vbox_service_event_code], 0
|
||
mov dword [vbox_service_thread_handle], 0
|
||
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_service_thread
|
||
; Основной service thread (bottom half обработки)
|
||
; =============================================================================
|
||
proc vbox_service_thread
|
||
DEBUGF 2, "[service] Service thread started\n"
|
||
|
||
.service_loop:
|
||
; 1. Ждем события или timeout
|
||
mov eax, [vbox_service_event]
|
||
mov ebx, [vbox_service_event_code]
|
||
; invoke WaitEventTimeout, eax, ebx, 1000 ; timeout 1000ms
|
||
invoke WaitEvent, eax, ebx
|
||
; 2. Проверяем, не пора ли завершать thread
|
||
cmp dword [vbox_service_running], 0
|
||
je .exit_thread
|
||
|
||
; 3. Обрабатываем pending events
|
||
call vbox_process_pending_events
|
||
|
||
; 4. Продолжаем цикл
|
||
jmp .service_loop
|
||
|
||
.exit_thread:
|
||
DEBUGF 2, "[service] Service thread exiting\n"
|
||
or eax, -1
|
||
int 0x40
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_process_pending_events
|
||
; Обработка всех pending events
|
||
; =============================================================================
|
||
proc vbox_process_pending_events
|
||
push ebx esi edi
|
||
|
||
.process_loop:
|
||
; 1. Атомарно читаем и сбрасываем pending events
|
||
cli
|
||
mov eax, [vbox_pending_events]
|
||
mov dword [vbox_pending_events], 0
|
||
sti
|
||
|
||
; 2. Если нет событий - выходим
|
||
test eax, eax
|
||
jz .done
|
||
|
||
DEBUGF 2, "[service] Processing events: 0x%x\n", eax
|
||
|
||
; 3. Сохраняем события для обработки
|
||
push eax
|
||
|
||
; =====================================================================
|
||
; Обработка DISPLAY_CHANGE (полная версия)
|
||
; =====================================================================
|
||
test eax, VMMDEV_EVENT_DISPLAY_CHANGE
|
||
jz .no_display_change
|
||
|
||
DEBUGF 2, "[service] Handling display change (full)\n"
|
||
call display_change
|
||
|
||
.no_display_change:
|
||
|
||
; =====================================================================
|
||
; Обработка HGCM (clipboard)
|
||
; =====================================================================
|
||
test eax, VMMDEV_EVENT_HGCM
|
||
jz .no_hgcm_event
|
||
|
||
; Проверяем, что clipboard инициализирован
|
||
cmp dword [clipboard_state.connected], 1
|
||
jne .hgcm_not_ready
|
||
|
||
DEBUGF 2, "[service] Handling HGCM clipboard event\n"
|
||
|
||
; Включаем прерывания на время обработки
|
||
sti
|
||
|
||
call clipboard_handle_event
|
||
|
||
; Выключаем обратно для атомарных операций
|
||
cli
|
||
|
||
jmp .no_hgcm_event
|
||
|
||
.hgcm_not_ready:
|
||
DEBUGF 2, "[service] HGCM event ignored (clipboard not ready)\n"
|
||
|
||
.no_hgcm_event:
|
||
|
||
; =====================================================================
|
||
; Обработка MOUSE events
|
||
; =====================================================================
|
||
test eax, VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
||
jz .no_mouse_event
|
||
|
||
DEBUGF 2, "[service] Handling mouse position change\n"
|
||
call mouse_position_changed
|
||
|
||
.no_mouse_event:
|
||
|
||
; 4. Восстанавливаем оригинальные события
|
||
pop eax
|
||
|
||
; 5. Проверяем, не появились ли новые события пока мы обрабатывали
|
||
; (это безопасно, т.к. мы уже обработали текущие)
|
||
jmp .process_loop
|
||
|
||
.done:
|
||
pop edi esi ebx
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: mouse_position_changed
|
||
; Обработка изменения позиции мыши
|
||
; =============================================================================
|
||
proc mouse_position_changed
|
||
push eax ebx ecx edx
|
||
|
||
; Читаем новые координаты мыши из MMIO
|
||
mov eax, [vbox_device.mmio]
|
||
test eax, eax
|
||
jz .no_mmio
|
||
|
||
; Здесь можно обновить состояние мыши в системе
|
||
|
||
.no_mmio:
|
||
pop edx ecx ebx eax
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_check_events
|
||
; Ручная проверка событий (для polling режима)
|
||
; =============================================================================
|
||
proc vbox_check_events
|
||
; Проверяем pending events вручную
|
||
mov eax, [vbox_pending_events]
|
||
test eax, eax
|
||
jz .no_events
|
||
|
||
; Если есть service thread, он обработает
|
||
cmp dword [vbox_service_thread_handle], 0
|
||
jne .thread_will_handle
|
||
|
||
; Иначе обрабатываем сами
|
||
call vbox_process_pending_events
|
||
|
||
.thread_will_handle:
|
||
.no_events:
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_clear_pending_events
|
||
; Очистка pending events
|
||
; =============================================================================
|
||
proc vbox_clear_pending_events
|
||
mov dword [vbox_pending_events], 0
|
||
ret
|
||
endp
|
||
|
||
; =============================================================================
|
||
; ПРОЦЕДУРА: vbox_get_pending_events
|
||
; Получение текущих pending events
|
||
; Возврат: EAX = pending events flags
|
||
; =============================================================================
|
||
proc vbox_get_pending_events
|
||
mov eax, [vbox_pending_events]
|
||
ret
|
||
endp
|