VBoxGuest драйвер и VBoxCtrl управление #352

Open
lex_coder wants to merge 2 commits from lex_coder/kolibrios:VBoxGuest into main
61 changed files with 11236 additions and 0 deletions
Showing only changes of commit ab59015ded - Show all commits

View File

@@ -0,0 +1,2 @@
include 'errors.inc'
include 'utils.inc'

View File

@@ -0,0 +1,82 @@
; =============================================================================
; VBoxGuest Driver for KolibriOS - Dual-level Error Handling
; VMMDev transport errors + HGCM service errors
; На базе версии v1_04
; =============================================================================
; =============================================================================
; VMMDev Error Codes (Generic VERR_*)
; These appear in vmmdev_request_header.rc
; =============================================================================
; Success codes (positive or zero)
VINF_SUCCESS equ 0
VINF_HGCM_ASYNC_EXECUTE equ 2903 ; Запрос выполняется асинхронно
VBOX_HGCM_REQ_DONE equ 0x00000001 ; Флаг завершения в поле flags
HGCM_TIMEOUT_DEFAULT equ 500000 ; Базовый таймаут
; err.h
VINF_NOT_SUPPORTED equ 37
VERR_ACCESS_DENIED equ -38
VERR_INTERRUPTED equ -39
VINF_INTERRUPTED equ 39
; =============================================================================
; Error Codes
; =============================================================================
VERR_TIMEOUT equ -78
VERR_NOT_READY equ -25
VERR_GENERAL_FAILURE equ -1
VERR_INVALID_PARAMETER equ -2
VERR_INVALID_MAGIC equ -3
VERR_INVALID_POINTER equ -6
VERR_NO_MEMORY equ -8
VERR_NOT_IMPLEMENTED equ -12
VERR_INVALID_FLAGS equ -13 ; 0xFFFFFFF3
VERR_INVALID_FUNCTION equ -36
VERR_NOT_SUPPORTED equ -37
VERR_TOO_MUCH_DATA equ -42
VERR_NOT_FOUND equ -78
VERR_INVALID_STATE equ -79
VERR_OUT_OF_RESOURCES equ -80
VERR_ALREADY_EXISTS equ -105
VERR_TRY_AGAIN equ -116
VERR_INTERNAL_ERROR equ -225
; VERR_WRONG_TYPE-22409
; VERR_WRONG_PARAMETER_TYPE-22416
VERR_NO_DATA equ -125 ; Нэт доступных данных
; =============================================================================
; HGCM Error Codes (Range -2900..-2909)
; These appear in VMMDevHGCMRequestHeader.result
; =============================================================================
VERR_HGCM_SERVICE_NOT_FOUND equ -2900
VERR_HGCM_CLIENT_REJECTED equ -2901
VERR_HGCM_INVALID_CMD_ADDRESS equ -2902
VERR_HGCM_INTERNAL equ -2904
VERR_HGCM_INVALID_CLIENT_ID equ -2905
VERR_HGCM_PROTOCOL_ERROR equ -2906
VERR_HGCM_TOO_MANY_CLIENTS equ -2908
VERR_HGCM_TOO_MANY_PARMS equ -2909
; =============================================================================
; Clipboard-specific Error Codes
; =============================================================================
VERR_SHCLPB_NO_DATA equ -7153
VERR_SHCLPB_FORMAT_NOT_SUPPORTED equ -7154
; =============================================================================
; Error Code Ranges
; =============================================================================
VMMDEV_ERROR_RANGE_START equ -1000
VMMDEV_ERROR_RANGE_END equ 0
HGCM_ERROR_RANGE_START equ -2910
HGCM_ERROR_RANGE_END equ -2900

View File

@@ -0,0 +1,133 @@
; =============================================================================
; VBoxGuest Driver for KolibriOS - Common Utilities
; Файл: common/utils.inc
; =============================================================================
; sf_utf8_to_cp866 Конвертировать UTF-8 строку в CP866 (кириллица)
proc sf_utf8_to_cp866 uses ebx ecx edx esi edi, src:dword, dst:dword, src_len:dword
mov esi, [src]
mov edi, [dst]
mov ecx, [src_len]
xor edx, edx
.u8_loop:
test ecx, ecx
jle .u8_done
movzx eax, byte [esi]
cmp al, 0x80
jb .u8_ascii
cmp al, 0xC0
jb .u8_skip1
cmp al, 0xE0
jb .u8_two_byte
cmp al, 0xF0
jb .u8_skip3
mov byte [edi], '?'
inc edi
inc edx
add esi, 4
sub ecx, 4
jmp .u8_loop
.u8_skip3:
mov byte [edi], '?'
inc edi
inc edx
add esi, 3
sub ecx, 3
jmp .u8_loop
.u8_ascii:
mov [edi], al
inc esi
inc edi
inc edx
dec ecx
jmp .u8_loop
.u8_two_byte:
cmp ecx, 2
jb .u8_done
movzx eax, byte [esi]
movzx ebx, byte [esi + 1]
and eax, 0x1F
shl eax, 6
and ebx, 0x3F
or eax, ebx
cmp eax, 0x0401
je .u8_yo_upper
cmp eax, 0x0451
je .u8_yo_lower
cmp eax, 0x0410
jb .u8_not_cyrillic
cmp eax, 0x041F
jbe .u8_upper1
cmp eax, 0x042F
jbe .u8_upper2
cmp eax, 0x043F
jbe .u8_lower1
cmp eax, 0x044F
jbe .u8_lower2
jmp .u8_not_cyrillic
.u8_upper1:
sub eax, 0x0410
add eax, 0x80
jmp .u8_store2
.u8_upper2:
sub eax, 0x0420
add eax, 0x90
jmp .u8_store2
.u8_lower1:
sub eax, 0x0430
add eax, 0xA0
jmp .u8_store2
.u8_lower2:
sub eax, 0x0440
add eax, 0xE0
jmp .u8_store2
.u8_yo_upper:
mov eax, 0xF0
jmp .u8_store2
.u8_yo_lower:
mov eax, 0xF1
jmp .u8_store2
.u8_not_cyrillic:
mov eax, '?'
.u8_store2:
mov [edi], al
add esi, 2
sub ecx, 2
inc edi
inc edx
jmp .u8_loop
.u8_skip1:
inc esi
dec ecx
jmp .u8_loop
.u8_done:
mov byte [edi], 0
mov eax, edx
ret
endp

View File

@@ -0,0 +1,41 @@
; =============================================================================
; VBoxGuest Конфигурация
; =============================================================================
; =============================================================================
; Автозапуск сервисов
; =============================================================================
AUTOSTART_MOUSE = 1 ; Абсолютная мыщъх
AUTOSTART_HEARTBEAT = 1 ; Heartbeat
AUTOSTART_DISPLAY = 1 ; Разрешение экрана
AUTOSTART_TIMESYNC = 0 ; Синхронизация времени
AUTOSTART_SHARED_FOLDERS = 1 ; Общие папки
AUTOSTART_CLIPBOARD = 1 ; Буфер обмена
AUTOSTART_GUEST_PROPS = 1 ; Guest Properties
AUTOSTART_SEAMLESS = 0 ; Seamless mode
; =============================================================================
; Настройки сервисов
; =============================================================================
SHFL_MAX_FOLDERS = 10 ; Максимум общих папок
; =============================================================================
; Отладка
; =============================================================================
; Уровни отладки:
; __DEBUG_LEVEL__ = 1 -> Полное логирование (очень много вывода)
; __DEBUG_LEVEL__ = 2 -> Только ошибки
__DEBUG__ = 1 ; Включена
__DEBUG_LEVEL__ = 2 ;
__DEBUG_IRQ__ = 0 ; Логирование прерываний
__DEBUG_HGCM__ = 0 ; Логирование HGCM
__DEBUG_SF__ = 0 ; SharedFolder
__DEBUG_CB__ = 0 ; ClipBoard
__DEBUG_EVENTS__ = 0 ; Логирование событий
__DEBUG_MOUSE__ = 0 ; Логирование мыщъх
__DEBUG_SEAMLESS__ = 0
__DEBUG_HB__ = 0 ; Heartbeat мониторинг гостя
__DEBUG_DISPLAY__ = 0 ; Display разрешение экрана
__DEBUG_DISPATCHER__ = 0 ; Логирование диспетчера

View File

@@ -0,0 +1,13 @@
; =============================================================================
; Модуль : Core Aggregator
; Назначение : Подключение всех модулей ядра драйвера
; Файл : core/core.inc
; =============================================================================
include 'core/state.inc'
include 'core/pci.inc'
include 'core/mmio.inc'
include 'core/ports.inc'
include 'core/irq.inc'
include 'core/timer.inc'
include 'core/dispatcher/dispatcher.inc'

View File

@@ -0,0 +1,345 @@
; =============================================================================
; Модуль : Service Dispatcher
; Файл : core/dispatcher.inc
; Назначение : Функции диспетчера сервисов
; =============================================================================
include 'dispatcher_macros.inc'
; =============================================================================
; Данные диспетчера
; =============================================================================
align 4
dispatcher_active_events dd 0 ; OR всех event_mask включенных сервисов
dispatcher_active_caps dd 0 ; OR всех caps_mask включенных сервисов
; dispatcher_find_by_id Найти сервис по ID
;
; Вход : svc_id числовой идентификатор сервиса
; Выход: eax = SERVICE_ENTRY* или 0 если не найден
proc dispatcher_find_by_id stdcall uses ecx esi, svc_id:dword
mov ecx, [services_count]
test ecx, ecx
jz .not_found
mov esi, services_table
mov eax, [svc_id]
.loop:
cmp [esi + SERVICE_ENTRY.id], eax
je .found
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.not_found:
xor eax, eax
ret
.found:
mov eax, esi
ret
endp
; Включить сервис по ID
proc dispatcher_enable_by_id stdcall, svc_id:dword
stdcall dispatcher_find_by_id, [svc_id]
test eax, eax
jz .not_found
stdcall dispatcher_enable_entry, eax
ret
.not_found:
mov eax, -1
ret
endp
; Выключить сервис по ID
proc dispatcher_disable_by_id stdcall, svc_id:dword
stdcall dispatcher_find_by_id, [svc_id]
test eax, eax
jz .not_found
stdcall dispatcher_disable_entry, eax
ret
.not_found:
mov eax, -1
ret
endp
; Включить сервис по указателю на SERVICE_ENTRY
proc dispatcher_enable_entry stdcall uses ebx esi, entry_ptr:dword
mov esi, [entry_ptr]
; Уже включен?
cmp dword [esi + SERVICE_ENTRY.enabled], 1
je .already_enabled
; Вызвать fn_enable если есть
mov eax, [esi + SERVICE_ENTRY.fn_enable]
test eax, eax
jz .no_enable_fn
push esi
call eax
pop esi
test eax, eax
jnz .enable_failed
.no_enable_fn:
; Включить
mov dword [esi + SERVICE_ENTRY.enabled], 1
; Добавить маски
mov eax, [esi + SERVICE_ENTRY.event_mask]
or [dispatcher_active_events], eax
mov eax, [esi + SERVICE_ENTRY.caps_mask]
or [dispatcher_active_caps], eax
DEBUGF 2, "[VBoxGuest] [Dispatcher] Enabled service ID=%d\n", [esi + SERVICE_ENTRY.id]
.already_enabled:
xor eax, eax
ret
.enable_failed:
DEBUGF 2, "[VBoxGuest] [Dispatcher] Enable failed: 0x%x\n", eax
ret
endp
; Выключить сервис по указателю
proc dispatcher_disable_entry stdcall uses ebx esi, entry_ptr:dword
mov esi, [entry_ptr]
; Уже выключен?
cmp dword [esi + SERVICE_ENTRY.enabled], 0
je .already_disabled
; Вызвать fn_disable если есть
mov eax, [esi + SERVICE_ENTRY.fn_disable]
test eax, eax
jz .no_disable_fn
push esi
call eax
pop esi
.no_disable_fn:
; Выключить
mov dword [esi + SERVICE_ENTRY.enabled], 0
; Пересчитать активные маски
call dispatcher_recalc_masks
DEBUGF 2, "[VBoxGuest] [Dispatcher] Disabled service ID=%d\n", [esi + SERVICE_ENTRY.id]
.already_disabled:
xor eax, eax
ret
endp
; dispatcher_disable_all Выключить все сервисы
proc dispatcher_disable_all uses ecx esi
DEBUGF 2, "[VBoxGuest] [Dispatcher] Disabling all services\n"
mov ecx, [services_count]
test ecx, ecx
jz .done
mov esi, services_table
.loop:
stdcall dispatcher_disable_entry, esi
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
ret
endp
; Инициализация всех сервисов
proc dispatcher_init_all uses ebx ecx esi
DEBUGF 2, "[VBoxGuest] [Dispatcher] Initializing %d services...\n", [services_count]
mov ecx, [services_count]
test ecx, ecx
jz .done
mov esi, services_table
.loop:
mov eax, [esi + SERVICE_ENTRY.fn_init]
test eax, eax
jz .next
DEBUGF 2, "[VBoxGuest] [Dispatcher] Init service ID=%d, name=%s\n", [esi + SERVICE_ENTRY.id], [esi + SERVICE_ENTRY.name_ptr]
push ecx esi
call eax
pop esi ecx
test eax, eax
jnz .init_failed
.next:
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
DEBUGF 2, "[VBoxGuest] [Dispatcher] All services initialized\n"
xor eax, eax
ret
.init_failed:
DEBUGF 2, "[VBoxGuest] [Dispatcher] Service ID=%d init failed: 0x%x\n", [esi + SERVICE_ENTRY.id], eax
jmp .next
endp
; Включить сервисы с autostart=1
proc dispatcher_enable_autostart uses ecx esi
DEBUGF 2, "[VBoxGuest] [Dispatcher] Enabling autostart services...\n"
mov ecx, [services_count]
test ecx, ecx
jz .done
mov esi, services_table
.loop:
cmp dword [esi + SERVICE_ENTRY.autostart], 1
jne .next
push ecx esi
stdcall dispatcher_enable_entry, esi
pop esi ecx
.next:
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
DEBUGF 2, "[VBoxGuest] [Dispatcher] Autostart done, events=0x%x, caps=0x%x\n", \
[dispatcher_active_events], [dispatcher_active_caps]
ret
endp
; Пересчитать активные маски
proc dispatcher_recalc_masks uses ebx ecx edx esi
xor edx, edx ; events
xor ebx, ebx ; caps
mov ecx, [services_count]
test ecx, ecx
jz .done
mov esi, services_table
.loop:
cmp dword [esi + SERVICE_ENTRY.enabled], 1
jne .next
or edx, [esi + SERVICE_ENTRY.event_mask]
or ebx, [esi + SERVICE_ENTRY.caps_mask]
.next:
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
mov [dispatcher_active_events], edx
mov [dispatcher_active_caps], ebx
ret
endp
; Разослать события включенным сервисам
proc dispatcher_dispatch stdcall uses ebx ecx edx esi, event_mask:dword
mov edx, [event_mask]
test edx, edx
jz .done
mov ecx, [services_count]
test ecx, ecx
jz .done
mov esi, services_table
.loop:
; Включен?
cmp dword [esi + SERVICE_ENTRY.enabled], 0
je .next
; Есть совпадение событий?
mov eax, [esi + SERVICE_ENTRY.event_mask]
test eax, edx
jz .next
; Есть обработчик?
mov eax, [esi + SERVICE_ENTRY.fn_on_event]
test eax, eax
jz .next
; Вызвать fn_on_event(event_mask)
push ecx edx esi
mov eax, edx
call [esi + SERVICE_ENTRY.fn_on_event]
pop esi edx ecx
.next:
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
ret
endp
; Вызвать fn_on_tick у включенных сервисов
proc dispatcher_tick_all uses eax ecx esi
mov ecx, [services_count]
test ecx, ecx
jz .done
mov esi, services_table
.loop:
cmp dword [esi + SERVICE_ENTRY.enabled], 0
je .next
mov eax, [esi + SERVICE_ENTRY.fn_on_tick]
test eax, eax
jz .next
push ecx esi
call eax
pop esi ecx
.next:
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
ret
endp
; Получить активную маску событий
proc dispatcher_get_active_events
mov eax, [dispatcher_active_events]
ret
endp
; Получить активную маску capabilities
proc dispatcher_get_active_caps
mov eax, [dispatcher_active_caps]
ret
endp

View File

@@ -0,0 +1,114 @@
; =============================================================================
; Модуль : Dispatcher Macros
; Файл : core/dispatcher_macros.inc
; Назначение : Макросы для авторегистрации сервисов VBoxGuest
; =============================================================================
; Структура SERVICE_ENTRY (52 байта = 13 DWORD)
struct SERVICE_ENTRY
id dd ? ; +0: Уникальный ID (auto-increment)
name_ptr dd ? ; +4: Указатель на строку имени
event_mask dd ? ; +8: Маска событий VMMDev
caps_mask dd ? ; +12: Маска guest capabilities
enabled dd ? ; +16: 0=выключен, 1=включен
autostart dd ? ; +20: 1=запуск с драйвером, 0=через vboxctrl
fn_init dd ? ; +24: Инициализация (или 0)
fn_enable dd ? ; +28: Включение (или 0)
fn_disable dd ? ; +32: Выключение (или 0)
fn_on_event dd ? ; +36: Обработчик IRQ (или 0)
fn_on_tick dd ? ; +40: Обработчик тика (или 0)
hgcm_wakeup_event dd ? ; +44: Event handle для пробуждения HGCM-треда из IRQ
hgcm_wakeup_event_id dd ? ; +48: Event euid для пробуждения HGCM-треда из IRQ
ends
; Алиас для кода использующего SERVICE_ENTRY_SIZE
SERVICE_ENTRY_SIZE = sizeof.SERVICE_ENTRY
; =============================================================================
; Инициализация списка сервисов (пустой)
; =============================================================================
__SERVICES_LIST__ equ
; =============================================================================
; REGISTER_SERVICE - Регистрация сервиса
; ID назначается автоматически (1, 2, 3, ...)
; =============================================================================
; Это позволяет корректно работать с IRP/forward
; =============================================================================
macro REGISTER_SERVICE svc_name, event_mask, caps_mask, fn_init, fn_enable, fn_disable, fn_on_event, fn_on_tick, autostart
{
; Добавляем в список с | как разделителем
; Формат: name|evmask|capmask|init|enable|disable|onevent|ontick|autostart
match any, __SERVICES_LIST__ \{
__SERVICES_LIST__ equ __SERVICES_LIST__,svc_name|event_mask|caps_mask|fn_init|fn_enable|fn_disable|fn_on_event|fn_on_tick|autostart
\}
match , __SERVICES_LIST__ \{
__SERVICES_LIST__ equ svc_name|event_mask|caps_mask|fn_init|fn_enable|fn_disable|fn_on_event|fn_on_tick|autostart
\}
}
; =============================================================================
; BUILD_SERVICE_TABLE - Генерация таблицы сервисов
; Создаёт:
; services_table - массив SERVICE_ENTRY
; services_table_end - метка конца
; services_count - количество сервисов (dd)
; SVC_ID_xxx - константы ID для каждого сервиса
; =============================================================================
macro BUILD_SERVICE_TABLE
{
align 4
services_table:
; Проверка: есть ли сервисы?
match , __SERVICES_LIST__ \{
display 'WARNING: No services registered!', 13, 10
\}
; Генерация записей
match list, __SERVICES_LIST__ \{
__BUILD_ENTRIES__ list
\}
services_table_end:
services_count dd __BUILD_ID__
}
; =============================================================================
; Вспомогательный макрос для построения записей
; =============================================================================
__BUILD_ID__ = 0
macro __BUILD_ENTRIES__ [entry]
{
forward
__BUILD_ID__ = __BUILD_ID__ + 1
; Парсим entry с разделителем |
; Формат: name|evmask|capmask|init|enable|disable|onevent|ontick|autostart
match _name|_evmask|_capmask|_init|_enable|_disable|_onevent|_ontick|_auto, entry \{
dd __BUILD_ID__ ; id
dd _name ; name_ptr
dd _evmask ; event_mask
dd _capmask ; caps_mask
dd 0 ; enabled = 0
dd _auto ; autostart
dd _init ; fn_init
dd _enable ; fn_enable
dd _disable ; fn_disable
dd _onevent ; fn_on_event
dd _ontick ; fn_on_tick
dd 0 ; hgcm_wakeup_event (заполняется сервисом в runtime)
dd 0 ; hgcm_wakeup_event_id
\}
}
; =============================================================================
; Макрос для получения ID по имени переменной (опционально)
; Использование: SVC_ID svc_name_var
; =============================================================================
macro SVC_ID name
{
; Поиск ID в рантайме через dispatcher_find_by_name
; Или использовать константу если известна
}

View File

@@ -0,0 +1,109 @@
; =============================================================================
; Модуль : IRQ Handler
; Назначение : Обработчик прерываний VMMDev
; Файл : core/irq.inc
; =============================================================================
align 4
vbox_irq_count dd 0
; vbox_irq_handler Top-half обработчик IRQ
proc vbox_irq_handler stdcall
push ebx edx
movzx edx, word [vbox_device.port]
add dx, VMMDEV_PORT_OFF_REQUEST_FAST
in eax, dx
test eax, eax
jz .not_ours
mov ebx, eax
; ACK все события сразу
out dx, eax
DEBUGF __DEBUG_IRQ__, "[VBoxGuest] [Dispatch] [IRQ] events=0x%x, service event=0x%x\n", ebx, [dispatcher_active_events]
; Немедленно разбудить HGCM-треды (Linux: wake_up() из IRQ)
test ebx, VMMDEV_EVENT_HGCM
jz .no_hgcm_wake
call hgcm_irq_dispatch
.no_hgcm_wake:
; Вызвать fn_on_event у всех подходящих сервисов прямо сейчас
stdcall dispatcher_dispatch, ebx
lock inc dword [vbox_irq_count]
.acked:
pop edx ebx
mov eax, 1
ret
.not_ours:
pop edx ebx
xor eax, eax
ret
endp
; hgcm_irq_dispatch Разбудить все HGCM-треды из IRQ контекста
proc hgcm_irq_dispatch
push eax ebx ecx edx esi edi
mov ecx, [services_count]
test ecx, ecx
jz .done
mov edi, services_table
.loop:
cmp dword [edi + SERVICE_ENTRY.enabled], 0
je .next
cmp dword [edi + SERVICE_ENTRY.hgcm_wakeup_event], 0
je .next
mov eax, [edi + SERVICE_ENTRY.hgcm_wakeup_event]
mov ebx, [edi + SERVICE_ENTRY.hgcm_wakeup_event_id]
xor edx, edx
xor esi, esi
invoke RaiseEvent
.next:
add edi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
pop edi esi edx ecx ebx eax
ret
endp
; vmmdev_irq_install Установить обработчик IRQ
proc vmmdev_irq_install
mov eax, [vbox_device.irq]
test eax, eax
jz .no_irq
DEBUGF 2, "[VBoxGuest] [IRQ] Attaching to IRQ %d\n", eax
invoke AttachIntHandler, eax, vbox_irq_handler, 0
test eax, eax
jz .failed
DEBUGF 2, "[VBoxGuest] [IRQ] Handler attached successfully\n"
xor eax, eax
ret
.no_irq:
DEBUGF 2, "[VBoxGuest] [IRQ] No IRQ line\n"
mov eax, VERR_GENERAL_FAILURE
ret
.failed:
DEBUGF 2, "[VBoxGuest] [IRQ] Attach failed\n"
mov eax, VERR_INTERNAL_ERROR
ret
endp

View File

@@ -0,0 +1,74 @@
; =============================================================================
; Модуль : Memory Mapped I/O Operations
; Назначение : Работа с MMIO областью VMMDev
; =============================================================================
proc mmio_map_vmmdev
; Маппинг физической памяти в виртуальное адресное пространство
invoke MapIoMem, [vbox_device.mmio_phys], VMMDEV_MEMORY_SIZE, PG_NOCACHE + PG_SW
test eax, eax
jz .map_failed
; Сохраняем виртуальный адрес
mov [vbox_device.mmio_virt], eax
DEBUGF 2, "[VBoxGuest] [MMIO] Mapped to virtual address: 0x%x\n", eax
; Проверяем версию VMMDev
call mmio_check_version
test eax, eax
jnz .version_check_failed
DEBUGF 2, "[VBoxGuest] [MMIO] Mapping completed successfully\n"
xor eax, eax
ret
.map_failed:
DEBUGF 2, "[VBoxGuest] [MMIO] ERROR: MapIoMem failed\n"
mov eax, VERR_NO_MEMORY
ret
.version_check_failed:
DEBUGF 2, "[VBoxGuest] [MMIO] ERROR: VMMDev version check failed\n"
ret
endp
; mmio_check_version Проверка версии VMMDev структуры в MMIO
proc mmio_check_version
mov edi, [vbox_device.mmio_virt]
; Отладочный вывод структуры VMMDev
DEBUGF 2, "[VBoxGuest] [MMIO] VMMDev structure at 0x%x:\n", edi
DEBUGF 2, "[VBoxGuest] [MMIO] size: 0x%x\n", [edi + VMMDEV_MEMORY.size]
DEBUGF 2, "[VBoxGuest] [MMIO] version: 0x%x\n", [edi + VMMDEV_MEMORY.version]
; Проверяем минимальный размер структуры
mov eax, [edi + VMMDEV_MEMORY.size]
cmp eax, sizeof.VMMDEV_MEMORY
jb .invalid_size
; Проверяем версию VMMDev
mov eax, [edi + VMMDEV_MEMORY.version]
DEBUGF 2, "[VBoxGuest] [MMIO] Detected VMMDev version: 0x%x (required: 0x%x)\n", \
eax, VMMDEV_MEMORY_VERSION
; Проверяем совместимость версии (должна быть >= требуемой)
cmp eax, VMMDEV_MEMORY_VERSION
jae .version_ok
; Устаревшая версия - выводим предупреждение, но продолжаем
DEBUGF 2, "[VBoxGuest] [MMIO] WARNING: Old VMMDev version: 0x%x (expected >= 0x%x)\n", \
eax, VMMDEV_MEMORY_VERSION
DEBUGF 2, "[VBoxGuest] [MMIO] Some features may be unavailable\n"
.version_ok:
DEBUGF 2, "[VBoxGuest] [MMIO] VMMDev version check passed\n"
xor eax, eax
ret
.invalid_size:
DEBUGF 2, "[VBoxGuest] [MMIO] ERROR: Invalid VMMDev structure size: %d (expected >= %d)\n", \
eax, sizeof.VMMDEV_MEMORY
mov eax, -1
ret
endp

View File

@@ -0,0 +1,117 @@
; =============================================================================
; Модуль : PCI Device Detection
; Назначение : Обнаружение и идентификация PCI устройства VirtualBox
; Файл : core/pci.inc
; =============================================================================
align 4
pci_device dd ? ; Указатель на структуру PCI устройства VBox
; vmmdev_probe Поиск PCI устройства VirtualBox в системе
proc vmmdev_probe
call pci_find_vmmdev
test eax, eax
jnz .fail
call pci_init_vmmdev_irq
test eax, eax
jnz .fail
call pci_init_vmmdev_bar
test eax, eax
jnz .fail
xor eax, eax
ret
.fail:
DEBUGF 2, "[VBoxGuest] [PCI] Initialization failed\n"
mov eax, VERR_GENERAL_FAILURE
ret
endp
proc pci_find_vmmdev
; Получаем список PCI устройств
invoke GetPCIList
mov ebx, eax ; EBX = начало списка (якорь)
.search_loop:
mov eax, [eax + PCIDEV.fd]
cmp eax, ebx
je .not_found
; Сравниваем Vendor/Device ID
mov edx, [eax + PCIDEV.vendor_device_id]
cmp edx, (VBOX_DEVICE_ID shl 16) + VBOX_VENDOR_ID
jne .search_loop
.found:
mov [pci_device], eax
DEBUGF 2, "[VBoxGuest] [PCI] VBox device found\n"
xor eax, eax
ret
.not_found:
DEBUGF 2, "[VBoxGuest] [PCI] VBox device NOT found\n"
mov eax, VERR_GENERAL_FAILURE
ret
endp
; pci_init_vmmdev_irq Чтение IRQ линии из PCI конфигурации
proc pci_init_vmmdev_irq
mov ebx, [pci_device]
DEBUGF 2, "[VBoxGuest] [PCI] pci_device=0x%x\n", [pci_device]
test ebx, ebx
jz .no_device
; Читаем IRQ линию из PCI конфигурации
invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.interrupt_line
movzx eax, al ; IRQ линия - младший байт
; Проверяем валидность IRQ (должна быть 0-15)
cmp eax, 16
jae .invalid_irq
; Сохраняем IRQ в структуре устройства
mov [vbox_device.irq], eax
DEBUGF 2, "[VBoxGuest] [PCI] IRQ line: %d\n", eax
xor eax, eax
ret
.no_device:
DEBUGF 2, "[VBoxGuest] [PCI] ERROR: No PCI device for IRQ init\n"
mov eax, VERR_GENERAL_FAILURE
ret
.invalid_irq:
DEBUGF 2, "[VBoxGuest] [PCI] ERROR: Invalid IRQ line: %d\n", eax
mov eax, VERR_GENERAL_FAILURE
ret
endp
; pci_init_vmmdev_bar Чтение BAR0 (I/O) и BAR1 (MMIO) из PCI конфигурации
proc pci_init_vmmdev_bar
mov ebx, [pci_device]
test ebx, ebx
jz .no_device
; Читаем BAR0 (I/O port)
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, "[VBoxGuest] [PCI] BAR0 (I/O Port): 0x%x\n", eax
; Читаем BAR1 (MMIO)
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, "[VBoxGuest] [PCI] BAR1 (MMIO): phys=0x%x\n", eax
xor eax, eax
ret
.no_device:
DEBUGF 2, "[VBoxGuest] [PCI] ERROR: No PCI device for BAR init\n"
mov eax, VERR_GENERAL_FAILURE
ret
endp

View File

@@ -0,0 +1,54 @@
; =============================================================================
; Модуль : VMMDev I/O Ports
; Назначение : Операции через I/O порты VMMDev (fast request/events)
; Файл : core/ports.inc
; =============================================================================
; -----------------------------------------------------------------------------
; vmmdev_send_request отправка физического адреса VMMDev-запроса в I/O порт
;
; Вход : phys_addr физ. адрес VMMDev-пакета
; Выход:
; -----------------------------------------------------------------------------
proc vmmdev_send_request uses edx, phys_addr:dword
mov eax, [phys_addr]
mov dx, [vbox_device.port]
out dx, eax
; Спин-ожидание (~1ms): даёт хосту время на обработку запроса
mov ecx, 1000000
.wait:
xor eax, eax
loop .wait
ret
endp
; -----------------------------------------------------------------------------
; ports_init проверка, что порт задан
;
; Выход: eax = 0 успех / VERR_INVALID_PARAMETER
; -----------------------------------------------------------------------------
proc ports_init
movzx eax, word [vbox_device.port]
test eax, eax
jz .bad
xor eax, eax
ret
.bad:
mov eax, VERR_INVALID_PARAMETER
ret
endp
; ports_read_fast_events прочитать pending mask через FAST порт
;
; Выход: eax = маска событий
proc ports_read_fast_events uses edx
mov dx, [vbox_device.port]
add dx, VMMDEV_PORT_OFF_REQUEST_FAST
in eax, dx
ret
endp

View File

@@ -0,0 +1,70 @@
; =============================================================================
; Модуль : Driver Internal Structures
; Назначение : Глобальная структура состояния драйвера VBoxGuest
; Файл : core/state.inc
; =============================================================================
struct VBOX_DEVICE
port dw ?
pad1 dw ?
mmio_virt dd ?
mmio_phys dd ?
irq dd ?
last_events dd ? ; последняя маска событий, прочитанная в IRQ
event_filter dd ?
caps dd ?
flags dd ?
; бит0: device_present
; бит1: mmio_mapped
; бит2: irq_attached
; бит3: timer_running
; HGCM
hgcm_timeout dd ?
; HGCM packets
hgcm_connect_virt dd ?
hgcm_connect_phys dd ?
hgcm_disconnect_virt dd ?
hgcm_disconnect_phys dd ?
hgcm_call_virt dd ?
hgcm_call_phys dd ?
; Pre-allocated packets
display_virt dd ?
display_phys dd ?
events_virt dd ?
events_phys dd ?
filter_virt dd ?
filter_phys dd ?
caps_virt dd ?
caps_phys dd ?
guestinfo_virt dd ?
guestinfo_phys dd ?
guestinfo2_virt dd ?
guestinfo2_phys dd ?
mouse_virt dd ?
mouse_phys dd ?
hypervisor_info_virt dd ?
hypervisor_info_phys dd ?
dnd_call_virt dd ?
dnd_call_phys dd ?
host_version_virt dd ?
host_version_phys dd ?
heartbeat_config_virt dd ?
heartbeat_config_phys dd ?
heartbeat_virt dd ?
heartbeat_phys dd ?
; Pagelist HGCM call buffer (динамически выделяется)
hgcm_call_pl_virt dd ?
hgcm_call_pl_phys dd ?
; Единый блок памяти для VMMDev пакетов
vmmdev_packets_page dd ?
ends

View File

@@ -0,0 +1,57 @@
; =============================================================================
; Модуль : Timer
; Назначение : Периодический вызов fn_on_tick сервисов
; Файл : core/timer.inc
; =============================================================================
align 4
vbox_timer_handle dd 0
TIMER_DELAY_START equ 10 ; 100ms до первого вызова
TIMER_INTERVAL equ 10 ; 100ms между вызовами
; timer_init Запуск таймера
proc timer_init
DEBUGF 2, "[VBoxGuest] [Timer] Initializing...\n"
mov eax, [vbox_timer_handle]
test eax, eax
jnz .ok
invoke TimerHS, TIMER_DELAY_START, TIMER_INTERVAL, timer_cb, 0
test eax, eax
jz .err
mov [vbox_timer_handle], eax
DEBUGF 2, "[VBoxGuest] [Timer] Started, handle=0x%x, interval=%dms\n", \
eax, TIMER_INTERVAL * 10
.ok:
xor eax, eax
ret
.err:
DEBUGF 2, "[VBoxGuest] [Timer] ERROR: Failed to start\n"
mov eax, VERR_INTERNAL_ERROR
ret
endp
; timer_stop Остановка таймера
proc timer_stop
mov eax, [vbox_timer_handle]
test eax, eax
jz .done
invoke CancelTimerHS, eax
mov dword [vbox_timer_handle], 0
DEBUGF 2, "[VBoxGuest] [Timer] Stopped\n"
.done:
ret
endp
; Callback таймера вызывает fn_on_tick у всех включенных сервисов
proc timer_cb stdcall, userdata:dword
stdcall dispatcher_tick_all
ret
endp

View File

@@ -0,0 +1,80 @@
; =============================================================================
; Clipboard Constants из VBox 7.2.6 исходников
;
; Источники:
; include/VBox/HostServices/VBoxClipboardSvc.h
; include/VBox/GuestHost/SharedClipboard.h
; =============================================================================
; =============================================================================
; Guest function numbers (VBOX_SHCL_GUEST_FN_*)
; Гость вызывает эти функции через HGCM
; =============================================================================
VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT equ 1 ; Ждать сообщение от хоста (blocking, deprecated но работает)
VBOX_SHCL_GUEST_FN_REPORT_FORMATS equ 2 ; Сообщить хосту о доступных форматах
VBOX_SHCL_GUEST_FN_DATA_READ equ 3 ; Прочитать данные с хоста
VBOX_SHCL_GUEST_FN_DATA_WRITE equ 4 ; Записать данные на хост
VBOX_SHCL_GUEST_FN_CONNECT equ 5 ; (deprecated в 7.x)
VBOX_SHCL_GUEST_FN_REPORT_FEATURES equ 6 ; Сообщить о поддерживаемых фичах
VBOX_SHCL_GUEST_FN_QUERY_FEATURES equ 7 ; Запросить фичи хоста
VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT equ 8 ; Peek без блокировки
VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT equ 9 ; Peek с блокировкой
VBOX_SHCL_GUEST_FN_MSG_GET equ 10 ; Получить сообщение (новый протокол)
VBOX_SHCL_GUEST_FN_MSG_CANCEL equ 26 ; Отменить ожидание
; Количества параметров
VBOX_SHCL_CPARMS_MSG_OLD_GET_WAIT equ 2
VBOX_SHCL_CPARMS_REPORT_FORMATS equ 1
VBOX_SHCL_CPARMS_DATA_READ equ 3
VBOX_SHCL_CPARMS_DATA_WRITE_OLD equ 2 ; Без CONTEXT_ID (старый протокол)
VBOX_SHCL_CPARMS_DATA_WRITE equ 3 ; С CONTEXT_ID (новый протокол)
; =============================================================================
; Host message types (VBOX_SHCL_HOST_MSG_*)
; Приходят в parm[0] от MSG_OLD_GET_WAIT
;
; КРИТИЧНО! В старом коде были НЕПРАВИЛЬНЫЕ значения:
; было: FORMATS_REPORT=1, READ_DATA=2, WRITE_DATA=3
; надо: QUIT=1, READ_DATA=2, FORMATS_REPORT=3
; =============================================================================
VBOX_SHCL_HOST_MSG_QUIT equ 1 ; Хост закрывает клипборд
VBOX_SHCL_HOST_MSG_READ_DATA equ 2 ; Хост хочет прочитать данные гостя
VBOX_SHCL_HOST_MSG_FORMATS_REPORT equ 3 ; Хост сообщает о новых форматах
VBOX_SHCL_HOST_MSG_CANCELED equ 4 ; Отмена
; =============================================================================
; Format flags (VBOX_SHCL_FMT_*) БИТОВЫЕ МАСКИ
;
; КРИТИЧНО! В старом коде были НЕПРАВИЛЬНЫЕ значения:
; было: FMT_TEXT=1, FMT_UNICODETEXT=13
; надо: FMT_UNICODETEXT=1 (bit 0), FMT_BITMAP=2 (bit 1), FMT_HTML=4 (bit 2)
; FMT_TEXT не существует в VBox! Только UNICODETEXT (UTF-16LE)
; =============================================================================
VBOX_SHCL_FMT_NONE equ 0x0000
VBOX_SHCL_FMT_UNICODETEXT equ 0x0001 ; bit 0 UTF-16LE текст
VBOX_SHCL_FMT_BITMAP equ 0x0002 ; bit 1 DIB bitmap
VBOX_SHCL_FMT_HTML equ 0x0004 ; bit 2 HTML
VBOX_SHCL_FMT_URI_LIST equ 0x0008 ; bit 3 URI list (drag-n-drop)
VBOX_SHCL_FMT_VALID_MASK equ 0x000F
; Буфер
VBOX_SHCL_MAX_CHUNK_SIZE equ 0x10000 ; 64KB
; Событие VMMDev для clipboard
CLIPBOARD_EVENT_MASK equ VMMDEV_EVENT_HGCM ; bit 1 = 0x02
CLIPBOARD_CAPS_MASK equ 0 ; VMMDEV_GUEST_SUPPORTS_SHCL ; bit 7 = 0x80
; =============================================================================
; Состояния listener'а
; =============================================================================
CLIP_LISTEN_IDLE equ 0 ; Не слушаем
CLIP_LISTEN_SUBMITTED equ 1 ; Запрос отправлен в VMMDev, ждём ответ
; Максимум ошибок подряд до отключения
CLIP_MAX_ERRORS equ 5
; Флаги KolibriOS события (для CreateEvent)
CLIP_MANUAL_DESTROY equ 0x80000000 ; Событие не уничтожается после WaitEvent
; VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or CLIPBOARD_CAPS_MASK )
; VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or CLIPBOARD_EVENT_MASK )

View File

@@ -0,0 +1,73 @@
; =============================================================================
; Clipboard (HGCM service: Shared Clipboard / SHCL)
; =============================================================================
struct SHCL_MSG_GET
header HGCM_CALL
msg_type HGCM_PARM
formats HGCM_PARM
ends
struct SHCL_FORMATS_REPORT
header HGCM_CALL
formats HGCM_PARM
ends
struct SHCL_DATA_READ
header HGCM_CALL
format HGCM_PARM
buffer HGCM_PARM
size HGCM_PARM
ends
struct SHCL_DATA_WRITE
header HGCM_CALL
format HGCM_PARM
buffer HGCM_PARM
ends
; =============================================================================
; Clipboard Structures
;
; ПРИМЕЧАНИЕ: Старые структуры SHCL_MSG_GET, SHCL_FORMATS_REPORT,
; SHCL_DATA_READ, SHCL_DATA_WRITE УДАЛЕНЫ они не нужны.
;
; Новый код использует плоский массив HGCM_PARM + hgcm_call32_pagelist,
; как в guest_props. Структуры с встроенным HGCM_CALL заголовком
; были нужны только для ручного формирования пакетов через
; hgcm_send_request, что больше не используется.
; =============================================================================
; struct CLIPBOARD_STATE
; client_id dd ? ; HGCM client ID
; connected dd ? ; 0 = not connected, 1 = connected
; formats_host dd ? ; Форматы доступные на хосте (битовая маска VBOX_SHCL_FMT_*)
; formats_guest dd ? ; Форматы доступные у гостя
; listen_state dd ?
; error_count dd ?
; ends
struct CLIPBOARD_STATE
client_id dd ? ; HGCM client ID
connected dd ? ; 0/1
listen_state dd ? ; IDLE/SUBMITTED
error_count dd ? ; Счетчик ошибок
formats_host dd ? ; Форматы от хоста
formats_guest dd ? ; Наши форматы
has_data dd ? ; Есть данные для чтения
data_size dd ? ; Размер данных
; Динамически выделяемые буферы
data_buf_ptr dd ? ; -> clip_data_buf (KernelAlloc 64K)
listen_pkt_virt dd ? ; -> clip_listen_pkt (KernelAlloc 4096, DMA)
listen_pkt_phys dd ? ; физ. адрес listen_pkt
parms_ptr dd ? ; -> clip_parms (KernelAlloc)
debug_buf_ptr dd ? ; -> clip_debug_buf (KernelAlloc)
; Поток слушателя (thread-based listener)
hgcm_event dd ? ; event handle (из CreateEvent)
hgcm_event_id dd ? ; event euid (из CreateEvent)
thread_id dd ? ; thread ID (из CreateThread)
thread_stop dd ? ; 1 = попросить поток завершиться
ends

View File

@@ -0,0 +1,135 @@
; =============================================================================
; Модуль : VBoxGuest Core Constants
; Файл : data/core/constants.inc
; Назначение : VMMDev константы и определения (PCI, версия, маски событий)
; =============================================================================
; PCI
VBOX_VENDOR_ID equ 0x80EE
VBOX_DEVICE_ID equ 0xCAFE
; VMMDev протокол v1_04
VMMDEV_VERSION_MAJOR equ 1
VMMDEV_VERSION_MINOR equ 4
VMMDEV_VERSION equ (VMMDEV_VERSION_MAJOR shl 16) or VMMDEV_VERSION_MINOR
VMMDEV_REQUEST_HEADER_VERSION equ 0x00010001
VMMDEV_HF_FAST_IRQ_ACK equ 0x00000001
VMMDEV_MEMORY_VERSION equ 1
VMMDEV_MEMORY_SIZE equ 0x00400000
VMMDEV_PORT_OFF_REQUEST equ 0
VMMDEV_PORT_OFF_REQUEST_FAST equ 8
VMMDEV_MAX_HGCM_PARMS equ 32
VMMDEV_MAX_HGCM_DATA_SIZE equ 128 ; MB
; VMMDev Request Types
VMMDEV_REQ_GET_HOST_VERSION equ 4
VMMDEV_REQ_ACKNOWLEDGE_EVENTS equ 41
VMMDEV_REQ_CTL_GUEST_FILTER_MASK equ 42
VMMDEV_REQ_REPORT_GUEST_INFO equ 50
VMMDEV_REQ_GET_DISPLAY_CHANGE_2 equ 54
VMMDEV_REPORT_GUEST_CAPS equ 55
VMMDEV_SET_GUEST_CAPS equ 56
VMMDEV_REQ_REPORT_GUEST_INFO2 equ 58
VMMDEV_HGCM_CONNECT equ 60
VMMDEV_HGCM_DISCONNECT equ 61
VMMDEV_HGCM_CALL32 equ 62
VMMDEV_HGCM_CALL64 equ 63
VBOXGSTINFO2_F_REQUESTOR_INFO equ 0x00000001
; Guest capabilities
VMMDEV_GUEST_SUPPORTS_SEAMLESS equ 0x00000001
VMMDEV_GUEST_SUPPORTS_GHW_MAPPING equ 0x00000002
VMMDEV_GUEST_SUPPORTS_GRAPHICS equ 0x00000004
VMMDEV_GUEST_SUPPORTS_VRDP equ 0x00000010
VMMDEV_GUEST_SUPPORTS_HGCM equ 0x00000020
VMMDEV_GUEST_SUPPORTS_ACPI equ 0x00000040
VMMDEV_GUEST_SUPPORTS_SHCL equ 0x00000080
VMMDEV_GUEST_SUPPORTS_VRDP_RESIZE equ 0x00000100
VMMDEV_GUEST_SUPPORTS_DRAG_AND_DROP equ 0x00000200
VMMDEV_GUEST_SUPPORTS_CR3_MONITORING equ 0x00000400
VMMDEV_GUEST_SUPPORTS_TIMER_NS equ 0x00000800
VMMDEV_GUEST_SUPPORTS_GUEST_HEARTBEAT equ 0x00001000
VMMDEV_GUEST_SUPPORTS_HOST_DISPLAY_TOPOLOGY equ 0x00002000
VMMDEV_GUEST_SUPPORTS_REQUESTOR equ 0x00004000
VMMDEV_GUEST_SUPPORTS_VBVA equ 0x00008000
VMMDEV_GUEST_SUPPORTS_SET_GUEST_CAPABILITIES equ 0x00010000
VMMDEV_GUEST_SUPPORTS_MOUSE equ 0x00020000
VMMDEV_GUEST_SUPPORTS_SHARED_FOLDERS equ 0x00040000
VMMDEV_GUEST_SUPPORTS_VIDEO_ACCEL equ 0x00080000
VMMDEV_GUEST_SUPPORTS_AUDIO equ 0x00100000
VMMDEV_GUEST_SUPPORTS_TSC_EMULATION equ 0x00200000
; VMMDev события (биты)
VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED equ 0x00000001 ; bit 0
VMMDEV_EVENT_HGCM equ 0x00000002 ; bit 1
VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST equ 0x00000004 ; bit 2
VMMDEV_EVENT_JUDGE_CREDENTIALS equ 0x00000008 ; bit 3
VMMDEV_EVENT_RESTORED equ 0x00000010 ; bit 4
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE equ 0x00000020 ; bit 5
VMMDEV_EVENT_BALLOON_CHANGE_REQUEST equ 0x00000040 ; bit 6
VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE equ 0x00000080 ; bit 7
VMMDEV_EVENT_VRDP equ 0x00000100 ; bit 8
VMMDEV_EVENT_MOUSE_POSITION_CHANGED equ 0x00000200 ; bit 9
VMMDEV_EVENT_CPU_HOTPLUG equ 0x00000400 ; bit 10
VMMDEV_EVENT_VALID_EVENT_MASK equ 0x000007FF ; bits 0-10
VMMDEV_EVENT_GUEST_HEARTBEAT equ 0x00040000
VMMDEV_EVENT_GUEST_HEARTBEAT_TIMEOUT equ 0x00080000
VMMDEV_EVENT_HGCM_ASYNC_CALL equ 0x00100000
; Накопительные маски событий и capabilities (сервисы добавляют через OR)
VBOXGUEST_EVENTS_OR_MASK equ 0
VBOXGUEST_EVENTS_NOT_MASK equ 0
VBOXGUEST_GUEST_CAPS_OR_MASK equ 0
VBOXGUEST_GUEST_CAPS_NOT_MASK equ 0
; Маска «шумных» событий НЕ логировать
DISPATCHER_NOISY_EVENTS equ (VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED or VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
; OS Type
VBOXOSTYPE_KOLIBRIOS equ 0x00090000
; HGCM размеры пакетов
HGCM_CALL_HEADER_SIZE equ 32
HGCM_CALL_BASE_SIZE equ 44
HGCM_PARAM_SIZE equ 12
; Host Features
VMMDEV_HVF_MMIO equ 0x00000002
; Memory Balloon
VMMDEV_MEMORY_BALLOON_CHUNK_SIZE equ 0x00100000
VMMDEV_MEMORY_BALLOON_CHUNK_PAGES equ 0x100
; Guest Facility Types
VBoxGuestFacilityType_VBoxGuestDriver equ 0
VBoxGuestFacilityType_VBoxService equ 1
VBoxGuestFacilityType_VBoxTrayClient equ 2
VBoxGuestFacilityType_Seamless equ 3
VBoxGuestFacilityType_Graphics equ 4
; Guest Facility Status
VBoxGuestFacilityStatus_Inactive equ 0
VBoxGuestFacilityStatus_Active equ 1
; Requestor flags
VMMDEV_REQUESTOR_USR_NOT_GIVEN equ 0x00000000
VMMDEV_REQUESTOR_USR_DRV equ 0x00000001
VMMDEV_REQUESTOR_USR_DRV_OTHER equ 0x00000002
VMMDEV_REQUESTOR_USR_ROOT equ 0x00000003
VMMDEV_REQUESTOR_USR_USER equ 0x00000006
VMMDEV_REQUESTOR_KERNEL equ 0x00000000
VMMDEV_REQUESTOR_USERMODE equ 0x00000008
VMMDEV_REQUESTOR_CON_DONT_KNOW equ 0x00000000
VMMDEV_REQUESTOR_CON_NO equ 0x00000010
VMMDEV_REQUESTOR_CON_YES equ 0x00000020
VMMDEV_REQUESTOR_GRP_VBOX equ 0x00000080
VMMDEV_REQUESTOR_TRUST_NOT_GIVEN equ 0x00000000
VMMDEV_REQUESTOR_VBOXGUEST equ (VMMDEV_REQUESTOR_USR_DRV or VMMDEV_REQUESTOR_GRP_VBOX)
; MMIO смещения
VMMDEV_MEMORY_HAVE_EVENTS_V1_04 equ 0x14
VMMDEV_MEMORY_HAVE_EVENTS_V1_03 equ 0x0C

View File

@@ -0,0 +1,297 @@
; =============================================================================
; VBoxGuest : Data / VMMDev структуры
; Назначение: ABI структуры протокола VMMDev (точное соответствие заголовкам VBox)
; Файл : data/core/structs.inc
; =============================================================================
; =============================================================================
; Базовый заголовок запросов VMMDev (VMMDevRequestHeader)
; =============================================================================
struct VMMDEV_HEADER
size dd ? ; 0 - размер пакета
version dd ? ; 4 - версия, = VMMDEV_REQUEST_HEADER_VERSION
request_type dd ? ; 8 - тип запроса
rc dd ? ; 12 - код возврата
reserved1 dd ? ; 16 - ВАЖНО! Это поле ДОЛЖНО быть, значение 0
f_requestor dd ? ; 20 - флаги requestor
ends ; Размер = 24 БАЙТА
; =============================================================================
; Структура VMMDevMemory v1_04
; =============================================================================
struct VMMDEV_MEMORY
size dd ? ; 0x00 размер области (в байтах)
version dd ? ; 0x04 версия структуры (u32Version)
have_events dd ?
; vb_va_memory VB_VA_MEMORY
ends
; VBVA memory layout => typedef struct VBVAMEMORY
struct VB_VA_MEMORY
; fu32_mode_flags dd ?
; off32_data dd ?
; off32_free dd ?
; au8_ring_buffer dd ? ; VMMDEV_VBVA_RING_BUFFER_SIZE
; VMMDEVVBVARECORD aRecords[VMMDEV_VBVA_MAX_RECORDS];
; index_record_first dd ?
; index_record_free dd ?
; fu32_supported_orders dd?
; reserved1 dd ? ; 0x0C: Зарезервировано
; host_features dd ? ; 0x10: Возможности хоста
; guest_features dd ? ; 0x14: Возможности гостя
; mouse_features dd ? ; 0x18: Возможности мыши
; mouse_pos_x dd ? ; 0x1C: Позиция мыши X
; mouse_pos_y dd ? ; 0x20: Позиция мыши Y
ends
; =============================================================================
; Запрос: Version Request
; =============================================================================
struct VMMDEV_GET_HOST_VERSION
header VMMDEV_HEADER
major dw ?
minor dw ?
build dd ?
revision dd ?
features dd ?
ends
; =============================================================================
; Запрос: ReportGuestInfo (VMMDevReq_ReportGuestInfo = 50)
; =============================================================================
struct VMMDEV_REPORT_GUEST_INFO
header VMMDEV_HEADER
interface_version dd ? ; VMMDEV_VERSION (0x00010004)
os_type dd ? ; VBOXOSTYPE_*
ends
; =============================================================================
; Запрос: ReportGuestInfo2 (VMMDevReq_ReportGuestInfo2 = 58)
; Используется в vgdrvReportGuestInfo
; =============================================================================
struct VMMDEV_GUEST_INFO2
additions_major dw ? ; VBOX_VERSION_MAJOR
additions_minor dw ? ; VBOX_VERSION_MINOR
additions_build dd ? ; VBOX_VERSION_BUILD
additions_revision dd ? ; VBOX_SVN_REV
additions_features dd ? ; VBOXGSTINFO2_F_REQUESTOR_INFO
szName rb 128 ; VBOX_VERSION_STRING
ends
struct VMMDEV_REPORT_GUEST_INFO2
header VMMDEV_HEADER
guest_info VMMDEV_GUEST_INFO2
ends
; =============================================================================
; Запрос: GetHypervisorInfo (VMMDevReq_GetHypervisorInfo = 2)
; =============================================================================
struct VMMDEV_REQ_HYPERVISOR_INFO
header VMMDEV_HEADER
hypervisorStart dd ? ; GCPhys32
hypervisorSize dd ?
ends
; =============================================================================
; Запрос: SetHypervisorInfo (VMMDevReq_SetHypervisorInfo = 3)
; =============================================================================
struct VMMDEV_SET_HYPERVISOR_INFO
header VMMDEV_HEADER
hypervisorStart dd ? ; GCPhys32
hypervisorSize dd ?
ends
; =============================================================================
; Запрос: AcknowledgeEvents (VMMDevReq_AcknowledgeEvents = 41)
; В коде: VMMDevEvents
; =============================================================================
struct VMMDEV_EVENTS
header VMMDEV_HEADER
events dd ? ; IN/OUT: маска событий
ends
; Алиас для совместимости
struct VMMDEV_ACKNOWLEDGE_EVENTS
header VMMDEV_HEADER
events dd ?
ends
; =============================================================================
; Запрос: CtlGuestFilterMask (VMMDevReq_CtlGuestFilterMask = 42)
; guestEventFilter = (guestEventFilter | u32OrMask) & ~u32NotMask
; =============================================================================
struct VMMDEV_CTL_GUEST_FILTER_MASK
header VMMDEV_HEADER
or_mask dd ? ; маска OR
not_mask dd ? ; маска NOT
ends
; =============================================================================
; Запрос: SetGuestCapabilities (VMMDevReq_SetGuestCapabilities = 55)
; guestCaps = (guestCaps | u32OrMask) & ~u32NotMask
; VMMDevReqGuestCapabilities2 = VMMDevReq_SetGuestCapabilities
; =============================================================================
struct VMMDEV_SET_GUEST_CAPABILITIES2
header VMMDEV_HEADER
or_mask dd ? ; маска OR
not_mask dd ? ; маска NOT
ends
; Алиас для совместимости
struct VMMDEV_GUEST_CAPS2
header VMMDEV_HEADER
or_mask dd ?
not_mask dd ?
ends
; =============================================================================
; Запрос: GetDisplayChangeRequest2 (VMMDevReq_GetDisplayChangeRequest2 = 54)
; =============================================================================
struct VMMDEV_GET_DISPLAY_CHANGE_REQUEST2
header VMMDEV_HEADER
x_res dd ?
y_res dd ?
bpp dd ?
event_ack dd ? ; BOOL: 0/1 (false/true)
display dd ? ; индекс дисплея (0 для основного)
ends
; =============================================================================
; Запрос: ReportGuestStatus (VMMDevReq_ReportGuestStatus = 61)
; =============================================================================
struct VBOXGUEST_STATUS
facility dd ? ; VBoxGuestFacilityType_*
status dd ? ; VBoxGuestFacilityStatus_*
flags dd ?
ends
struct VMMDEV_REPORT_GUEST_STATUS
header VMMDEV_HEADER
guestStatus VBOXGUEST_STATUS
ends
; =============================================================================
; Memory Ballooning Structures
; =============================================================================
; Запрос: GetMemBalloonChangeRequest
struct VMMDEV_GET_MEM_BALLOON_CHANGE_REQUEST
header VMMDEV_HEADER
eventAck dd ?
cBalloonChunks dd ?
cPhysMemChunks dd ?
ends
; Запрос: ChangeMemBalloon
struct VMMDEV_CHANGE_MEM_BALLOON
header VMMDEV_HEADER
cPages dd ?
fInflate dd ? ; true = inflate, false = deflate
aPhysPage rd VMMDEV_MEMORY_BALLOON_CHUNK_PAGES
ends
; =============================================================================
; Вспомогательные структуры
; =============================================================================
; SHFLSTRING - строка с длиной (UTF-8 или UTF-16)
struct SHFLSTRING
size dw ? ; Размер буфера в байтах
length dw ? ; Длина строки в байтах (без null terminator)
string rb 0 ; Начало строки (переменный размер)
ends
; SHFLFSOBJINFO - информация о файле/директории (92 байта)
; Источник: include/iprt/types.h RTFSOBJINFO
struct SHFLFSOBJINFO
size_low dd ? ; +0: cbObject low (int64_t)
size_high dd ? ; +4: cbObject high
allocated_low dd ? ; +8: cbAllocated low (int64_t)
allocated_high dd ? ; +12: cbAllocated high
access_time_low dd ? ; +16: AccessTime low (RTTIMESPEC, nanoseconds)
access_time_high dd ? ; +20: AccessTime high
modification_time_low dd ? ; +24: ModificationTime low
modification_time_high dd ? ; +28: ModificationTime high
change_time_low dd ? ; +32: ChangeTime low
change_time_high dd ? ; +36: ChangeTime high
birth_time_low dd ? ; +40: BirthTime low
birth_time_high dd ? ; +44: BirthTime high
fMode dd ? ; +48: RTFMODE (тип+права: 0x4000=dir, 0x8000=file)
attr_reserved rb 40 ; +52: остаток RTFSOBJATTR (enmAdditional + union)
ends ; = 48 + 4 + 40 = 92 байт
; SHFLMAPPING - информация о mapping (для QUERY_MAPPINGS)
struct SHFLMAPPING
flags dd ? ; Флаги
root dd ? ; Root handle
; После этого идет SHFLSTRING с именем
ends
; =============================================================================
; Internal Driver Structures
; =============================================================================
struct VBOXGUEST_WAIT
Event dd ? ; RTSEMEVENTMULTI handle
ListNode_next dd ? ; RTListNode::pNext
ListNode_prev dd ? ; RTListNode::pPrev
fReqEvents dd ? ; запрашиваемые события
fResEvents dd ? ; полученные события
pSession dd ? ; сессия
pHGCMReq dd ? ; HGCM запрос (если есть)
fPendingWakeUp dd ? ; флаг отложенного пробуждения
fFreeMe dd ? ; флаг для освобождения
ends
struct VBOXGUEST_SESSION
Process dd ? ; RTPROCESS
R0Process dd ? ; RTR0PROCESS
pDevExt dd ? ; устройство
fRequestor dd ? ; VMMDEV_REQUESTOR_*
fUserSession dd ? ; bool
SessionSpinlock dd ? ; спинлок для синхронизации сессии
ListNode_next dd ? ; RTListNode::pNext
ListNode_prev dd ? ; RTListNode::pPrev
fEventFilter dd ? ; маска фильтра событий
fMouseStatus dd ? ; статус мыши
fCapabilities dd ? ; возможности
fAcquiredGuestCaps dd ? ; приобретенные возможности
aHGCMClientIds rd 32 ; HGCM client IDs
fPendingCancelWaitEvents dd ? ; bool
u32MousePosChangedSeq dd ? ; последовательность изменения позиции мыши
ends
; =============================================================================
; Memory Ballooning Internal Structures
; =============================================================================
struct VBOXGUEST_MEMBALLOON
hMtx dd ? ; мьютекс
cChunks dd ? ; текущее количество чанков
cMaxChunks dd ? ; максимальное количество чанков
fUseKernelAPI dd ? ; использовать ли API ядра
paMemObj dd ? ; указатель на массив объектов памяти
pOwner dd ? ; владелец (сессия)
ends
; =============================================================================
; Bit Usage Tracker Structure
; =============================================================================
struct VBOXGUEST_BITUSAGE_TRACKER
acPerBitUsage rd 32 ; счетчики использования битов (0..31)
fMask dd ? ; итоговая маска
ends
struct DISPLAY_STATE
width dd ?
height dd ?
bpp dd ?
lfb dd ?
pitch dd ?
refresh_rate dd ?
active dd ?
pending_change dd ?
x dd ?
y dd ?
ends

View File

@@ -0,0 +1,26 @@
; =============================================================================
; VBoxGuest : Data / VMMDev константы структуры
; Назначение: Агрегатор все констант и структур
; Файл : data/data.inc
; =============================================================================
include 'core\constants.inc'
include 'core\structs.inc'
include 'hgcm\constants.inc'
include 'hgcm\structs.inc'
include 'heartbeat\constants.inc'
include 'heartbeat\structs.inc'
include 'mouse\constants.inc'
include 'mouse\structs.inc'
include 'display\constants.inc'
include 'display\structs.inc'
include 'timesync\constants.inc'
include 'timesync\structs.inc'
include 'guest_props\constants.inc'
include 'guest_props\structs.inc'
include 'shared_folders\constants.inc'
include 'shared_folders\structs.inc'
include 'clipboard\constants.inc'
include 'clipboard\structs.inc'
include 'seamless\constants.inc'
include 'seamless\structs.inc'

View File

@@ -0,0 +1,33 @@
; =============================================================================
; Модуль : Display Service Constants
; Файл : data/display/constants.inc
; Назначение : Параметры автоизменения разрешения экрана VMMDev Display Change
; =============================================================================
; Display resolution limits
DISP_W_MIN equ 640
DISP_H_MIN equ 480
DISP_W_MAX equ 3840
DISP_H_MAX equ 2160
; BGA (Bochs Graphics Adapter) register ports
VBE_DISPI_IOPORT_INDEX equ 0x01CE
VBE_DISPI_IOPORT_DATA equ 0x01CF
; BGA register indices
VBE_DISPI_INDEX_XRES equ 0x01
VBE_DISPI_INDEX_YRES equ 0x02
VBE_DISPI_INDEX_BPP equ 0x03
VBE_DISPI_INDEX_ENABLE equ 0x04
; BGA enable flags
VBE_DISPI_DISABLED equ 0x00
VBE_DISPI_ENABLED equ 0x01
VBE_DISPI_LFB_ENABLED equ 0x40
; Event mask for display service
DISPLAY_EVENT_MASK equ VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
; Accumulate global masks
VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or VMMDEV_GUEST_SUPPORTS_GRAPHICS )
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or DISPLAY_EVENT_MASK )

View File

@@ -0,0 +1,17 @@
; =============================================================================
; Display Service Structures
; =============================================================================
; VMMDEV_GET_DISPLAY_CHANGE_REQUEST2 and DISPLAY_STATE are defined in
; data/core/structs.inc
struct DISPLAY
x dd ?
y dd ?
width dd ?
height dd ?
bits_per_pixel dd ?
vrefresh dd ?
pitch dd ?
lfb dd ?
ends

View File

@@ -0,0 +1,39 @@
; =============================================================================
; Модуль : Guest Properties Constants
; Файл : data/guest_props/constants.inc
; Назначение : HGCM сервис "VBoxGuestPropSvc" функции и константы
; =============================================================================
; HGCM function numbers (guest side)
GUEST_PROP_FN_GET_PROP equ 1 ; Получить свойство
GUEST_PROP_FN_SET_PROP equ 2 ; Установить свойство (с флагами)
GUEST_PROP_FN_DEL_PROP equ 4 ; ?? 3 ; Удалить свойство
GUEST_PROP_FN_SET_PROP_VALUE equ 3 ; ?? 4 ; Установить значение (без флагов)
GUEST_PROP_FN_ENUM_PROPS equ 5 ; Перечислить свойства
GUEST_PROP_FN_GET_NOTIFICATION equ 6 ; Ожидать уведомления об изменении
; GUEST_PROP_FN_HOST_SET_PROP_VALUE equ 4 ; недоступно гостю
; Максимальные размеры
GUEST_PROP_MAX_NAME_LEN equ 256
GUEST_PROP_MAX_VALUE_LEN equ 1024
GUEST_PROP_MAX_FLAGS_LEN equ 128
GUEST_PROP_ENUM_BUF_SIZE equ 4096
; Количество HGCM параметров для разных операций
GUEST_PROP_GET_PARM_COUNT equ 4 ; name, value, timestamp, flags
GUEST_PROP_SET_PARM_COUNT equ 3 ; name, value, flags
GUEST_PROP_SET_VALUE_PARM_COUNT equ 2 ; name, value
GUEST_PROP_DEL_PARM_COUNT equ 1 ; name
GUEST_PROP_ENUM_PARM_COUNT equ 3 ; patterns, buffer, size
; Стандартные свойства для установки при инициализации
; /VirtualBox/GuestInfo/OS/Product = "KolibriOS"
; /VirtualBox/GuestInfo/OS/Release = "1.0"
; /VirtualBox/GuestAdd/VersionExt = "1.0.0"
; /VirtualBox/GuestAdd/Revision = "1"
; Маска событий: Guest Properties не используют VMMDev события напрямую
GUEST_PROP_EVENT_MASK equ 0
; Capabilities: не требует отдельных guest caps
GUEST_PROP_CAPS_MASK equ 0

View File

@@ -0,0 +1,16 @@
; =============================================================================
; Guest Properties Structures
; =============================================================================
struct GUEST_PROP_STATE
client_id dd ? ; HGCM client ID
connected dd ? ; 0/1
; Динамически выделяемые буферы
enum_buf_ptr dd ? ; gp_enum_buf (KernelAlloc 4096)
name_buf_ptr dd ? ; gp_name_buf (часть small_bufs)
value_buf_ptr dd ? ; gp_value_buf
flags_buf_ptr dd ? ; gp_flags_buf
parms_ptr dd ? ; gp_parms
small_bufs_ptr dd ? ; единый блок (KernelAlloc 4096)
ends

View File

@@ -0,0 +1,11 @@
; =============================================================================
; Модуль : Heartbeat Service Constants
; Файл : data/heartbeat/constants.inc
; Назначение : VMMDev heartbeat мониторинг гостя (запросы и события)
; =============================================================================
VMMDEV_REQ_GUEST_HEARTBEAT equ 217 ; VMMDevReq_GuestHeartbeat, Отправка heartbeat
VMMDEV_REQ_HEARTBEAT_CONFIGURE equ 218 ; VMMDevReq_HeartbeatConfigure, Настройка heartbeat
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or VMMDEV_EVENT_GUEST_HEARTBEAT or VMMDEV_EVENT_GUEST_HEARTBEAT_TIMEOUT)
VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or VMMDEV_GUEST_SUPPORTS_GUEST_HEARTBEAT )

View File

@@ -0,0 +1,20 @@
; =============================================================================
; Запрос: HeartbeatConfigure (VMMDevReq_HeartbeatConfigure = 60)
; Структура VMMDevReqHeartbeat
; cNsInterval - /* OUT: интервал в наносекундах */
; f_enabled - флаг включения
; =============================================================================
struct VMMDEV_HEARTBEAT_CONFIGURE
header VMMDEV_HEADER
c_ns_interval dd ? ; Интервал в наносекундах (младшая часть)
c_ns_interval_high dd ? ; Интервал в наносекундах (старшая часть)
f_enabled dd ? ; bool(byte) + выравнивание, 1 = включить, 0 = выключить
ends ; 24 + 12 байт
; AssertCompileSize(VMMDevReqHeartbeat, 24+12);
; =============================================================================
; Запрос: GuestHeartbeat (VMMDevReq_GuestHeartbeat = 61)
; =============================================================================
struct VMMDEV_GUEST_HEARTBEAT
header VMMDEV_HEADER
ends ; 24 bytes

View File

@@ -0,0 +1,107 @@
; =============================================================================
; Модуль : HGCM Protocol Constants
; Файл : data/hgcm/constants.inc
; Назначение : HGCM (Host Guest Communication Manager) протокол константы и параметры
; =============================================================================
; =============================================================================
; HGCM Protocol Constants
; =============================================================================
VBOX_HGCM_REQ_DONE equ 0x00000001
VBOX_HGCM_REQ_CANCELLED equ 0x00000002
HGCM_SERVICE_NAME_MAX equ 128
HGCM_TIMEOUT_ITERS equ 500000 ; Количество итераций внешнего цикла ожидания
HGCM_ASYNC_DELAY_ITERS equ 1000 ; Количество pause инструкций во вложенном цикле
HGCM_LOC_TYPE_PREDEFINED equ 2
; =============================================================================
; HGCM Parameter Types (VMMDevHGCMParmType)
; Источник: include/VBox/VMMDev.h из исходного кода VirtualBox
; =============================================================================
VMMDEV_HGCM_PARM_TYPE_INVALID equ 0
VMMDEV_HGCM_PARM_TYPE_32BIT equ 1
HGCM_PARM_TYPE_32BIT equ VMMDEV_HGCM_PARM_TYPE_32BIT
VMMDEV_HGCM_PARM_TYPE_64BIT equ 2
; VMMDEV_HGCM_PARM_TYPE_PHYSADDR equ 3 ; устарел
VMMDEV_HGCM_PARM_TYPE_LINADDR equ 4 ; In and Out
HGCM_PARM_TYPE_LINADDR equ VMMDEV_HGCM_PARM_TYPE_LINADDR
VMMDEV_HGCM_PARM_TYPE_LINADDR_IN equ 5 ; Host <- Guest
HGCM_PARM_TYPE_LINADDR_IN equ VMMDEV_HGCM_PARM_TYPE_LINADDR_IN
VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT equ 6 ; Host -> Guest
HGCM_PARM_TYPE_LINADDR_OUT equ VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT
VMMDEV_HGCM_PARM_TYPE_LINADDR_LOCKED equ 7 ; Для VBoxGuest, а не хоста
VMMDEV_HGCM_PARM_TYPE_LINADDR_LOCKED_IN equ 8 ; Для VBoxGuest, а не хоста
VMMDEV_HGCM_PARM_TYPE_LINADDR_LOCKED_OUT equ 9 ; Для VBoxGuest, а не хоста
VMMDEV_HGCM_PARM_TYPE_PAGELIST equ 10
VMMDEV_HGCM_PARM_TYPE_EMBEDDED equ 11
VMMDEV_HGCM_PARM_TYPE_CONTIGUOUS_PAGELIST equ 12
VMMDEV_HGCM_PARM_TYPE_NO_BOUNCE_PAGELIST equ 13
VMMDEV_HGCM_PARM_TYPE_SIZE_HACK equ 0x7fffffff
; =============================================================================
; Флаги для EMBEDDED параметров (Embedded.fFlags)
; =============================================================================
VMMDEV_HGCM_EMBEDDED_FLAG_IN equ 1 ; Данные от гостя к хосту
VMMDEV_HGCM_EMBEDDED_FLAG_OUT equ 2 ; Данные от хоста к гостю
VMMDEV_HGCM_EMBEDDED_FLAG_BOTH equ 3 ; (IN | OUT)
; =============================================================================
; Дополнительные константы для проверки возможностей хоста (Host Features)
; =============================================================================
VMMDEV_HVF_HGCM_EMBEDDED_BUFFERS equ 0x00000002 ; Битовый флаг 1
; =============================================================================
; (Опционально) Для обратной совместимости с вашим текущим кодом
; =============================================================================
; Короткие алиасы для использования в коде:
HGCM_PARM_TYPE_64BIT equ VMMDEV_HGCM_PARM_TYPE_64BIT
HGCM_PARM_TYPE_LINADDR_INOUT equ VMMDEV_HGCM_PARM_TYPE_LINADDR ; = 4 (bidirectional)
HGCM_PARM_TYPE_EMBEDDED equ VMMDEV_HGCM_PARM_TYPE_EMBEDDED
HGCM_EMBED_FLAG_IN equ VMMDEV_HGCM_EMBEDDED_FLAG_IN
HGCM_EMBED_FLAG_OUT equ VMMDEV_HGCM_EMBEDDED_FLAG_OUT
HGCM_EMBED_FLAG_BOTH equ VMMDEV_HGCM_EMBEDDED_FLAG_BOTH
; Максимальный размер данных для embedded
HGCM_MAX_EMBEDDED_DATA equ 4096
; Максимальный хвост PageList (для pagelist call buffer)
HGCM_MAX_PAGELIST_TAIL equ 8192
; =============================================================================
; Константы PageList
; =============================================================================
HGCM_PARM_TYPE_PAGELIST equ 10
; Флаги направления PageList
HGCM_PL_FLAG_IN equ 1 ; guest -> host
HGCM_PL_FLAG_OUT equ 2 ; host -> guest
HGCM_PL_FLAG_BOTH equ 3
; Размеры страниц
PAGE_SIZE equ 4096
PAGE_OFFSET_MASK equ 0x0FFF
PAGE_BASE_MASK equ 0xFFFFF000
; Смещения внутри HGCMPageListInfo
HGCM_PLI_FLAGS equ 0 ; dd flags
HGCM_PLI_OFF_FIRST_PAGE equ 4 ; dw offFirstPage
HGCM_PLI_CPAGES equ 6 ; dw cPages
HGCM_PLI_APAGES equ 8 ; начало массива dq[] aPages
; =============================================================================
; Макрос FILL_HGCM_HEADER - Заполнить стандартный заголовок HGCM пакета
; Параметры:
; reg - регистр с указателем на структуру HGCM_HEADER
; size - размер пакета в байтах
; =============================================================================
macro FILL_HGCM_HEADER reg, size {
mov dword [reg + HGCM_HEADER.header.size], size
mov dword [reg + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [reg + HGCM_HEADER.header.request_type], VMMDEV_REQ_HGCM_CALL32
mov dword [reg + HGCM_HEADER.header.rc], VERR_GENERAL_FAILURE
mov dword [reg + HGCM_HEADER.header.reserved1], 0
mov dword [reg + HGCM_HEADER.header.requestor], VMMDEV_REQUESTOR_KERNEL
}

View File

@@ -0,0 +1,70 @@
; =============================================================================
; VBoxGuest Driver for KolibriOS - Structures HGCM
; =============================================================================
; =============================================================================
; HGCM Structures
; =============================================================================
struct HGCM_HEADER
header VMMDEV_HEADER
flags dd ?
result dd ?
ends ; 32 байта
struct HGCM_CONNECT
header HGCM_HEADER
location_type dd ?
service_name rb HGCM_SERVICE_NAME_MAX ; имя сервиса
client_id dd ?
ends
struct HGCM_DISCONNECT
header HGCM_HEADER
client_id dd ?
ends
struct HGCM_CALL
header HGCM_HEADER ; 32
client_id dd ? ; +4 = 36 // ID клиента
function dd ? ; +4 = 40 // Номер функции (1=MSG_OLD_GET_WAIT и т.д.)
param_count dd ? ; +4 = 44 // Количество параметров
; params[0] начинаются с offset 44
; Далее идут параметры: struct vmmdev_hgcm_function_parameter params[parm_count];
ends
struct HGCM_PARM
type dd ? ; тип параметра (VMMDevHGCMParmType)
u rb 8 ; union { uint32_t value32; RTGCPTR pointer; }
ends
HGCM_PARM.u.value32 equ HGCM_PARM.u
HGCM_PARM.u.value_or_size equ HGCM_PARM.u
HGCM_PARM.u.value64_lo equ HGCM_PARM.u
HGCM_PARM.u.value64_hi equ HGCM_PARM.u+4
HGCM_PARM.u.LinAddr.size equ HGCM_PARM.u
HGCM_PARM.u.LinAddr.offset equ HGCM_PARM.u+4
HGCM_PARM.value_or_size equ HGCM_PARM.u
HGCM_PARM.offset_or_addr equ HGCM_PARM.u+4
; sizeof(vmmdev_request_header) = 24
; sizeof(vmmdev_hgcmreq_header) = 32 // header(24) + flags(4) + result(4)
; sizeof(vmmdev_hgcm_call) = 44 // header(32) + client_id(4) + function(4) + parm_count(4)
; sizeof(vmmdev_hgcm_function_parameter) = 12
; sizeof(vmmdev_hgcm_pagelist) = 16 + (cPages * 8)
; struct vmmdev_hgcm_pagelist {
; u32 flags; // Direction flags (1,2,3)
; u16 offFirstPage; // Смещение в первой странице
; u16 cPages; // Количество страниц
; u64 pages[1]; // Массив физических адресов страниц (RTGCPHYS64)
; };
; Размер буфера для PageList HGCM вызовов
; = sizeof.HGCM_CALL(44) + 32*sizeof.HGCM_PARM(12) + 8192 = 8620 3 страницы
HGCM_PL_BUF_SIZE equ sizeof.HGCM_CALL + (VMMDEV_MAX_HGCM_PARMS * sizeof.HGCM_PARM) + HGCM_MAX_PAGELIST_TAIL

View File

@@ -0,0 +1,40 @@
; =============================================================================
; Модуль : Mouse Service Constants
; Файл : data/mouse/constants.inc
; Назначение : VMMDev запросы мыши (абсолютные координаты, кнопки, скролл)
; =============================================================================
VMMDEV_REQ_GET_MOUSE_STATUS equ 1
VMMDEV_REQ_SET_MOUSE_STATUS equ 2
VMMDEV_REQ_SET_POINTER_SHAPE equ 3
VMMDEV_REQ_GET_MOUSE_STATUS_EX equ 223 ; Extended: + buttons, scroll
; VMMDEV_REQ_GET_POINTER_SHAPE equ 58
; Новые (v1_04):
VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE equ 0x00000001
VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR equ 0x00000002
VMMDEV_MOUSE_GUEST_HAS_ABSOLUTE equ 0x00000004
VMMDEV_MOUSE_NEW_PROTOCOL equ 0x00000010
VMMDEV_MOUSE_NOTIFY_GUEST equ 0x00000080
VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE equ 0x00000100
VMMDEV_MOUSE_HOST_HAS_ABSOLUTE equ 0x00000200
VMMDEV_MOUSE_HOST_NEW_PROTOCOL equ 0x00000400
; Full state protocol (VBox 6.1+): кнопки + скролл в одном запросе
VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL equ 0x00000080
VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL equ 0x00000100
; Mouse button masks
VMMDEV_MOUSE_BUTTON_LEFT equ 0x01
VMMDEV_MOUSE_BUTTON_RIGHT equ 0x02
VMMDEV_MOUSE_BUTTON_MIDDLE equ 0x04
VMMDEV_MOUSE_BUTTON_X1 equ 0x08
VMMDEV_MOUSE_BUTTON_X2 equ 0x10
VBOX_MOUSE_GUEST_FEATURES equ VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
VBOX_MOUSE_GUEST_FEATURES_EXT equ (VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE or VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL)
MOUSE_EVENT_MASK equ ( VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED or VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or VMMDEV_GUEST_SUPPORTS_MOUSE )
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or MOUSE_EVENT_MASK )

View File

@@ -0,0 +1,43 @@
; =============================================================================
; Запрос: GetMouseStatus (VMMDevReq_GetMouseStatus = 1)
; Запрос: SetMouseStatus (VMMDevReq_SetMouseStatus = 2)
; =============================================================================
struct VMMDEV_REQ_MOUSE_STATUS
header VMMDEV_HEADER
mouse_features dd ? ; VMMDEV_MOUSE_* features
pointer_x_pos dd ? ; X position
pointer_y_pos dd ? ; Y position
ends
; =============================================================================
; Mouse pointer shape (для изменения курсора)
; =============================================================================
struct VMMDEV_REQ_POINTER_SHAPE
header VMMDEV_HEADER
flags dd ? ; VMMDEV_POINTER_* flags
x_hot dd ? ; X hotspot
y_hot dd ? ; Y hotspot
width dd ? ; Width in pixels
height dd ? ; Height in pixels
; За которыми следуют данные маски
ends
; =============================================================================
; Extended mouse status (VMMDevReq_GetMouseStatusEx = 223)
; Включает кнопки и скролл. Требует VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL.
; =============================================================================
struct VMMDEV_REQ_MOUSE_STATUS_EX
core VMMDEV_REQ_MOUSE_STATUS
scroll_dz dd ? ; vertical scroll delta
scroll_dw dd ? ; horizontal scroll delta
buttons dd ? ; VMMDEV_MOUSE_BUTTON_* mask
ends
;struct VMMDEV_REQ_MOUSE_POINTER
; header VMMDEV_HEADER
; f_flags dd ?
; x_hot dd ?
; y_hot dd ?
; width dd ?
; height dd ?
; data rb 0 ; AND-mask + XOR-mask (variable)
;ends

View File

@@ -0,0 +1,26 @@
; =============================================================================
; Seamless Constants VBox 7.2.6
; Источник: include/VBox/VMMDevCoreTypes.h, include/VBox/VMMDev.h
; =============================================================================
; VMMDevReq_GetSeamlessChangeRequest
VMMDEV_REQ_GET_SEAMLESS_CHANGE equ 73
; VMMDevSeamlessMode enum
VMMDEV_SEAMLESS_DISABLED equ 0 ; Обычный режим, весь десктоп
VMMDEV_SEAMLESS_VISIBLE_REGION equ 1 ; Только top-level окна
VMMDEV_SEAMLESS_HOST_WINDOW equ 2 ; Каждое окно гостя = окно хоста
; VMMDevReq_VideoSetVisibleRegion
VMMDEV_REQ_VIDEO_SET_VISIBLE_REGION equ 72
; Event: хост просит переключить seamless mode
; VMMDEV_EVENT_SEAMLESS_MODE_CHANGE = 0x00000020 (bit 5) уже в core/constants.inc
SEAMLESS_EVENT_MASK equ VMMDEV_EVENT_SEAMLESS_MODE_CHANGE
; Caps: гость поддерживает seamless
; VMMDEV_GUEST_SUPPORTS_SEAMLESS = 0x00000001 уже в core/constants.inc
; Накопительная маска
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or SEAMLESS_EVENT_MASK )

View File

@@ -0,0 +1,29 @@
; =============================================================================
; Seamless Structures VBox 7.2.6
; Источник: include/VBox/VMMDev.h
; =============================================================================
; VMMDevSeamlessChangeRequest (VMMDevReq_GetSeamlessChangeRequest = 73)
; size = 24 (header) + 8 = 32
struct VMMDEV_SEAMLESS_CHANGE_REQUEST
header VMMDEV_HEADER
mode dd ? ; OUT: VMMDevSeamlessMode (0=disabled, 1=visible, 2=host_window)
eventAck dd ? ; IN: VMMDEV_EVENT_SEAMLESS_MODE_CHANGE для ACK
ends
; RTRECT прямоугольник (для VideoSetVisibleRegion)
struct RTRECT
xLeft dd ?
yTop dd ?
xRight dd ?
yBottom dd ?
ends
; VMMDevVideoSetVisibleRegion (VMMDevReq_VideoSetVisibleRegion = 72)
; Переменная длина: header(24) + cRect(4) + Rect[cRect] (16 каждый)
; Для KolibriOS один прямоугольник (весь экран)
struct VMMDEV_VIDEO_SET_VISIBLE_REGION
header VMMDEV_HEADER
cRect dd ? ; Количество прямоугольников
rect0 RTRECT ; Первый (и единственный) прямоугольник
ends

View File

@@ -0,0 +1,113 @@
; =============================================================================
; SharedFolder Constants (VirtualBox HGCM)
; =============================================================================
; =========================================
; Функции службы Shared Folders (SHFL_FN_*)
; =========================================
SHFL_FN_QUERY_MAPPINGS equ 1 ; Запросить список подключенных папок
SHFL_FN_QUERY_MAP_NAME equ 2 ; Запросить имя подключения по индексу
SHFL_FN_CREATE equ 3 ; Создать файл/каталог
SHFL_FN_CLOSE equ 4 ; Закрыть дескриптор
SHFL_FN_READ equ 5 ; Чтение из файла
SHFL_FN_WRITE equ 6 ; Запись в файл
SHFL_FN_LOCK equ 7 ; Блокировка файла
SHFL_FN_LIST equ 8 ; Получить содержимое каталога
SHFL_FN_INFORMATION equ 9 ; Получить/установить информацию о файле
SHFL_FN_UNUSED_10 equ 10 ; (Зарезервировано)
SHFL_FN_REMOVE equ 11 ; Удалить файл/каталог
SHFL_FN_MAP_FOLDER_OLD equ 12 ; Устаревшее подключение папки (не использовать)
SHFL_FN_UNMAP_FOLDER equ 13 ; Отключить папку
SHFL_FN_RENAME equ 14 ; Переименовать/переместить
SHFL_FN_FLUSH equ 15 ; Сбросить кэш файла на диск
SHFL_FN_SET_UTF8 equ 16 ; Включить режим UTF-8 (0 параметров)
SHFL_FN_MAP_FOLDER equ 17 ; Подключить папку
SHFL_FN_READLINK equ 18 ; Прочитать символьную ссылку
SHFL_FN_SYMLINK equ 19 ; Создать символьную ссылку
SHFL_FN_SET_SYMLINKS equ 20 ; Разрешить/запретить симлинки (0 параметров)
SHFL_FN_SET_FILE_SIZE equ 24 ; Установить размер файла (усечь/расширить)
; Флаги для SHFL_FN_REMOVE
SHFL_REMOVE_FILE equ 0x01 ; Удалить файл
SHFL_REMOVE_DIR equ 0x02 ; Удалить каталог
SHFL_REMOVE_SYMLINK equ 0x04 ; Удалить символьную ссылку
; Флаги для SHFL_FN_RENAME
SHFL_RENAME_FILE equ 0x01 ; Переименовать файл
SHFL_RENAME_DIR equ 0x02 ; Переименовать каталог
SHFL_RENAME_REPLACE_IF_EXISTS equ 0x04 ; Заменить если существует
; Флаги для операции LIST (используются в SHFL_LIST_PARMS.Flags)
; ==============================================================
SHFL_LIST_NONE equ 0x00000000 ; Базовый листинг
SHFL_LIST_RETURN_ONE equ 0x00000001 ; Вернуть только первую запись
SHFL_LIST_RESTART equ 0x00000002 ; Начать перебор заново
SHFL_LIST_SIZE_RETURNED equ 0x00000004 ; В SizeReturned реальный размер данных
; Лимиты
; ======
SHFL_MAX_MAPPINGS equ 10 ; Максимальное число подключенных папок
SHFL_MAX_NAME_LEN equ 256 ; Макс. длина имени в символах
SHFL_MAX_PATH_LEN equ 4096 ; Макс. длина пути в байтах (включая UTF-8)
; Флаги создания/открытия файлов - SHFL_CF_*
; ======================================================================
; SHFL_CF_* CreateFlags для SHFL_FN_CREATE (из shflsvc.h)
; ======================================================================
SHFL_CF_NONE equ 0x00000000
; Бит 0: Lookup only
SHFL_CF_LOOKUP equ 0x00000001
; Бит 1: Open target directory
SHFL_CF_OPEN_TARGET_DIRECTORY equ 0x00000002
; Бит 2: Object is a directory
SHFL_CF_DIRECTORY equ 0x00000004
; Биты 4..7: Action if file EXISTS
SHFL_CF_ACT_MASK_IF_EXISTS equ 0x000000F0
SHFL_CF_ACT_OPEN_IF_EXISTS equ 0x00000000 ; Open existing
SHFL_CF_ACT_FAIL_IF_EXISTS equ 0x00000010 ; Fail if exists
SHFL_CF_ACT_REPLACE_IF_EXISTS equ 0x00000020 ; Replace (truncate)
SHFL_CF_ACT_OVERWRITE_IF_EXISTS equ 0x00000030 ; Overwrite
; Биты 8..11: Action if file is NEW
SHFL_CF_ACT_MASK_IF_NEW equ 0x00000F00
SHFL_CF_ACT_CREATE_IF_NEW equ 0x00000000 ; Create new file
SHFL_CF_ACT_FAIL_IF_NEW equ 0x00000100 ; Fail if doesn't exist
; Биты 12..13: Access mode (read/write)
SHFL_CF_ACCESS_MASK_RW equ 0x00003000
SHFL_CF_ACCESS_NONE equ 0x00000000
SHFL_CF_ACCESS_READ equ 0x00001000
SHFL_CF_ACCESS_WRITE equ 0x00002000
SHFL_CF_ACCESS_READWRITE equ 0x00003000
; Биты 14..15: Deny mode (sharing)
SHFL_CF_ACCESS_MASK_DENY equ 0x0000C000
SHFL_CF_ACCESS_DENYNONE equ 0x00000000
SHFL_CF_ACCESS_DENYREAD equ 0x00004000
SHFL_CF_ACCESS_DENYWRITE equ 0x00008000
SHFL_CF_ACCESS_DENYALL equ 0x0000C000
; Биты 16..17: Attribute access
SHFL_CF_ACCESS_MASK_ATTR equ 0x00030000
SHFL_CF_ACCESS_ATTR_NONE equ 0x00000000
SHFL_CF_ACCESS_ATTR_READ equ 0x00010000
SHFL_CF_ACCESS_ATTR_WRITE equ 0x00020000
SHFL_CF_ACCESS_ATTR_READWRITE equ 0x00030000
; Бит 18: Append mode
SHFL_CF_ACCESS_APPEND equ 0x00040000
; ======================================================================
; SHFLCREATERESULT результат CREATE
; ======================================================================
SHFL_NO_RESULT equ 0
SHFL_PATH_NOT_FOUND equ 1
SHFL_FILE_NOT_FOUND equ 2
SHFL_FILE_EXISTS equ 3
SHFL_FILE_CREATED equ 4
SHFL_FILE_REPLACED equ 5

View File

@@ -0,0 +1,304 @@
; =============================================================================
; VirtualBox SharedFolder Structures
; =============================================================================
; -----------------------------------------------------------------------------
; Глобальное состояние SharedFolder драйвера
; -----------------------------------------------------------------------------
struct SF_STATE
connected dd ? ; 1 = HGCM подключен к сервису
client_id dd ? ; HGCM client ID
; Динамически выделяемые init-буферы
packet_ptr dd ? ; sf_packet (KernelAlloc 4096)
mappings_ptr dd ? ; sf_mappings (часть small_bufs)
namebuf_ptr dd ? ; sf_namebuf
cp866_buf_ptr dd ? ; sf_cp866_buf
listbuf_ptr dd ? ; sf_listbuf (KernelAlloc 4096)
createparms_ptr dd ? ; sf_createparms
dir_path_ptr dd ? ; sf_dir_path
small_bufs_ptr dd ? ; единый блок мелких буферов (KernelAlloc 4096)
; Динамически выделяемые FS-буферы
fs_packet_ptr dd ? ; vboxsf_fs_packet (KernelAlloc 4096)
fs_listbuf_ptr dd ? ; vboxsf_fs_listbuf (KernelAlloc 4096)
fs_iobuf_ptr dd ? ; vboxsf_fs_iobuf (KernelAlloc 65536)
fs_cparms_ptr dd ? ; vboxsf_fs_cparms (KernelAlloc 4096)
fs_pathbuf_ptr dd ? ; vboxsf_fs_pathbuf
fs_pathbuf2_ptr dd ? ; vboxsf_fs_pathbuf2
fs_tmpname_ptr dd ? ; vboxsf_fs_tmpname
fs_small_bufs_ptr dd ? ; единый блок мелких FS-буферов (KernelAlloc 4096)
ends
; -----------------------------------------------------------------------------
; Информация об одной shared folder (выделяется через KernelAlloc)
; -----------------------------------------------------------------------------
struct SF_FOLDER
root_handle dd ? ; SHFL root handle (от MAP_FOLDER)
active dd ? ; 1 = folder активен и подключен
name rb 256 ; Имя mapping (UTF-8, null-terminated)
ends
; -----------------------------------------------------------------------------
; Данные для виртуального диска /vbox/
; -----------------------------------------------------------------------------
struct VBOXSF_DISK
folders rd SHFL_MAX_FOLDERS ; Указатели на SF_FOLDER
count dd ? ; Количество папок
disk_handle dd ? ; Handle от DiskAdd
ends
; =============================================================================
; Структура DISKMEDIAINFO для querymedia callback
; =============================================================================
struct DISKMEDIAINFO
flags dd ? ; флаги медиа
sectorsize dd ? ; размер сектора
capacity dq ? ; емкость в секторах (64-bit)
ends
; =============================================================================
; Смещения внутренних структур ядра KolibriOS
; (нужны для create_partition callback)
; =============================================================================
KPARTITION_OFS_DISK = 16 ; PARTITION.Disk -> DISK*
KPARTITION_OFS_FSUSERFUNCTIONS = 20 ; PARTITION.FSUserFunctions -> UserFuncs*
KDISK_OFS_USERDATA = 16 ; DISK.UserData -> void*
; =============================================================================
; BDFE (Block Data File Entry) формат записи в директории KolibriOS
; =============================================================================
BDFE_SIZE = 304 ; Размер одной BDFE записи (CP866)
BDFE_HEADER_SIZE = 32 ; Размер заголовка ответа ReadFolder
; Структура заголовка ReadFolder ответа
struct BDFE_HEADER
version dd ? ; Версия формата (1)
entries_placed dd ? ; Количество возвращенных записей
total_entries dd ? ; Общее количество записей
reserved rd 5 ; Зарезервировано (20 байт)
ends
; Смещения полей BDFE
BDFE_OFS_ATTR = 0 ; dword: атрибуты
BDFE_OFS_ENCODING = 4 ; byte: кодировка (0=CP866, 1=UTF-16, 2=UTF-8)
BDFE_OFS_CTIME = 8 ; dword: время создания
BDFE_OFS_CDATE = 12 ; dword: дата создания
BDFE_OFS_ATIME = 16 ; dword: время доступа
BDFE_OFS_ADATE = 20 ; dword: дата доступа
BDFE_OFS_MTIME = 24 ; dword: время модификации
BDFE_OFS_MDATE = 28 ; dword: дата модификации
BDFE_OFS_SIZE_LO = 32 ; dword: размер файла (младшие 32 бита)
BDFE_OFS_SIZE_HI = 36 ; dword: размер файла (старшие 32 бита)
BDFE_OFS_NAME = 40 ; 264 байта: имя файла (null-terminated)
; Атрибуты файлов
FA_READONLY = 0x01
FA_HIDDEN = 0x02
FA_SYSTEM = 0x04
FA_LABEL = 0x08
FA_FOLDER = 0x10
FA_ARCHIVED = 0x20
; Коды ошибок KolibriOS (syscall 70)
ERROR_SUCCESS = 0
ERROR_DISK_FULL = 1
ERROR_UNSUPPORTED = 2
ERROR_UNKNOWN_FS = 3
ERROR_FILE_NOT_FOUND = 5
ERROR_END_OF_FILE = 6
ERROR_MEMORY = 7
ERROR_ACCESS_DENIED = 10
ERROR_DEVICE = 11
; =============================================================================
; Смещения внутри SHFLFSOBJINFO (92 байта)
; Используются для конвертации в BDFE при ReadFolder/GetFileInfo
; =============================================================================
SHFLOBJINFO_OFS_SIZE_LO = 0 ; int64 cbObject (low)
SHFLOBJINFO_OFS_SIZE_HI = 4 ; int64 cbObject (high)
SHFLOBJINFO_OFS_ALLOC_LO = 8 ; int64 cbAllocated (low)
SHFLOBJINFO_OFS_ALLOC_HI = 12 ; int64 cbAllocated (high)
SHFLOBJINFO_OFS_BTIME_LO = 16 ; int64 BirthTime (low, nanosec)
SHFLOBJINFO_OFS_BTIME_HI = 20 ; int64 BirthTime (high)
SHFLOBJINFO_OFS_CTIME_LO = 24 ; int64 ChangeTime (low)
SHFLOBJINFO_OFS_CTIME_HI = 28 ; int64 ChangeTime (high)
SHFLOBJINFO_OFS_MTIME_LO = 32 ; int64 ModificationTime (low)
SHFLOBJINFO_OFS_MTIME_HI = 36 ; int64 ModificationTime (high)
SHFLOBJINFO_OFS_ATIME_LO = 40 ; int64 AccessTime (low)
SHFLOBJINFO_OFS_ATIME_HI = 44 ; int64 AccessTime (high)
SHFLOBJINFO_OFS_FMODE = 48 ; uint32 Attr (RTFMODE)
; Биты fMode (RTFMODE)
S_IFDIR = 0x4000
S_IFREG = 0x8000
; =============================================================================
; HGCM пакеты для SharedFolder операций
; =============================================================================
; -----------------------------------------------------------------------------
; SHFL_FN_QUERY_MAPPINGS - Получить список доступных mappings
; -----------------------------------------------------------------------------
struct SHFL_QUERY_MAPPINGS
header HGCM_CALL
flags HGCM_PARM ; IN: флаги (обычно 0)
count HGCM_PARM ; OUT: количество mappings
buffer HGCM_PARM ; OUT: буфер для данных (SHFLMAPPING[])
ends
; -----------------------------------------------------------------------------
; SHFL_FN_QUERY_MAP_NAME - Получить имя mapping по индексу
; -----------------------------------------------------------------------------
struct SHFL_QUERY_MAP_NAME
header HGCM_CALL
index HGCM_PARM ; IN: индекс mapping (0..count-1)
name HGCM_PARM ; OUT: имя mapping (SHFLSTRING)
ends
; -----------------------------------------------------------------------------
; SHFL_FN_MAP_FOLDER - Подключить mapping
; -----------------------------------------------------------------------------
struct SHFL_MAP_FOLDER
header HGCM_CALL
path HGCM_PARM ; IN
root HGCM_PARM ; OUT
delimiter HGCM_PARM ; IN
flags HGCM_PARM ; IN
ends
; -----------------------------------------------------------------------------
; SHFL_FN_UNMAP_FOLDER - Отключить mapping
; -----------------------------------------------------------------------------
struct SHFL_UNMAP_FOLDER
header HGCM_CALL
root HGCM_PARM ; IN: root handle
ends
; -----------------------------------------------------------------------------
; SHFL_FN_CREATE - Открыть/создать файл
; -----------------------------------------------------------------------------
struct SHFL_CREATE
header HGCM_CALL
root HGCM_PARM ; IN: root handle
path HGCM_PARM ; IN: путь к файлу (SHFLSTRING)
parms HGCM_PARM ; IN/OUT: параметры (SHFLCREATEPARMS)
ends
; Параметры создания файла (108 байт)
struct SHFLCREATEPARMS
handle_lo dd ? ; +0: OUT: SHFLHANDLE low (uint64_t)
handle_hi dd ? ; +4: OUT: SHFLHANDLE high
result dd ? ; +8: OUT: SHFLCREATERESULT
flags dd ? ; +12: IN: SHFL_CF_* CreateFlags
info rb 92 ; +16: IN/OUT: SHFLFSOBJINFO
ends
; -----------------------------------------------------------------------------
; SHFLDIRINFO - одна запись в результате SHFL_FN_LIST (переменный размер)
; Фиксированная часть = 126 байт:
; SHFLFSOBJINFO (92) + cucShortName (2) + uszShortName (28) + SHFLSTRING hdr (4)
; За ней идут байты имени файла (переменная длина).
; -----------------------------------------------------------------------------
SHFLDIRINFO_FIXED_SIZE = 126
SHFLDIRINFO_OFS_INFO = 0 ; SHFLFSOBJINFO (92 bytes)
SHFLDIRINFO_OFS_SIZE_LO = 0 ; cbObject low
SHFLDIRINFO_OFS_SIZE_HI = 4 ; cbObject high
SHFLDIRINFO_OFS_FMODE = 48 ; fMode (RTFMODE)
SHFLDIRINFO_OFS_CUC_SHORT_NAME = 92 ; cucShortName (uint16)
SHFLDIRINFO_OFS_USZ_SHORT_NAME = 94 ; uszShortName (14 x uint16 = 28 bytes)
SHFLDIRINFO_OFS_NAME_SIZE = 122 ; SHFLSTRING.u16Size
SHFLDIRINFO_OFS_NAME_LENGTH = 124 ; SHFLSTRING.u16Length
SHFLDIRINFO_OFS_NAME_STRING = 126 ; SHFLSTRING.string (данные имени)
; -----------------------------------------------------------------------------
; SHFL_FN_CLOSE - Закрыть файл
; -----------------------------------------------------------------------------
struct SHFL_CLOSE
header HGCM_CALL
root HGCM_PARM ; IN: root handle
handle HGCM_PARM ; IN: file handle
ends
; -----------------------------------------------------------------------------
; SHFL_FN_READ - Читать из файла
; -----------------------------------------------------------------------------
struct SHFL_READ
header HGCM_CALL
root HGCM_PARM ; IN: root handle
handle HGCM_PARM ; IN: file handle
offset_low HGCM_PARM ; IN: offset low (64-bit)
offset_high HGCM_PARM ; IN: offset high (64-bit)
size HGCM_PARM ; IN/OUT: размер для чтения
buffer HGCM_PARM ; OUT: буфер для данных
ends
; -----------------------------------------------------------------------------
; SHFL_FN_WRITE - Писать в файл
; -----------------------------------------------------------------------------
struct SHFL_WRITE
header HGCM_CALL
root HGCM_PARM ; IN: root handle
handle HGCM_PARM ; IN: file handle
offset_low HGCM_PARM ; IN: offset low (64-bit)
offset_high HGCM_PARM ; IN: offset high (64-bit)
size HGCM_PARM ; IN/OUT: размер для записи
buffer HGCM_PARM ; IN: буфер с данными
ends
; -----------------------------------------------------------------------------
; SHFL_FN_LIST - Получить список файлов в директории
; -----------------------------------------------------------------------------
struct SHFL_LIST
header HGCM_CALL
root HGCM_PARM ; parm[0] IN: root handle (32bit)
handle HGCM_PARM ; parm[1] IN: dir handle (64bit!)
flags HGCM_PARM ; parm[2] IN: SHFL_LIST_* (32bit)
cb HGCM_PARM ; parm[3] IN/OUT: размер буфера (32bit)
path HGCM_PARM ; parm[4] IN: search pattern (опционально)
buffer HGCM_PARM ; parm[5] OUT: SHFLDIRINFO[]
resume_pt HGCM_PARM ; parm[6] IN/OUT: точка продолжения (32bit)
file_count HGCM_PARM ; parm[7] OUT: количество файлов (32bit)
ends
; -----------------------------------------------------------------------------
; SHFL_FN_INFORMATION - Получить информацию о файле
; -----------------------------------------------------------------------------
struct SHFL_INFORMATION
header HGCM_CALL
root HGCM_PARM ; IN: root handle
handle HGCM_PARM ; IN: file handle
flags HGCM_PARM ; IN: флаги
info HGCM_PARM ; OUT: информация (SHFLFSOBJINFO)
ends
; -----------------------------------------------------------------------------
; SHFL_FN_REMOVE - Удалить файл
; -----------------------------------------------------------------------------
struct SHFL_REMOVE
header HGCM_CALL
root HGCM_PARM ; IN: root handle
path HGCM_PARM ; IN: путь к файлу (SHFLSTRING)
flags HGCM_PARM ; IN: флаги
ends
; -----------------------------------------------------------------------------
; SHFL_FN_RENAME - Переименовать файл
; -----------------------------------------------------------------------------
struct SHFL_RENAME
header HGCM_CALL
root HGCM_PARM ; IN: root handle
src HGCM_PARM ; IN: исходный путь (SHFLSTRING)
dst HGCM_PARM ; IN: новый путь (SHFLSTRING)
flags HGCM_PARM ; IN: флаги
ends
; -----------------------------------------------------------------------------
; SHFL_FN_FLUSH - Flush буферов файла
; -----------------------------------------------------------------------------
struct SHFL_FLUSH
header HGCM_CALL
root HGCM_PARM ; IN: root handle
handle HGCM_PARM ; IN: file handle
ends

View File

@@ -0,0 +1,16 @@
; =============================================================================
; Time Sync Constants
; =============================================================================
VMMDEV_REQ_GET_HOST_TIME equ 10 ; VMMDevReq_GetHostTime
; Интервал синхронизации: 6000 тиков * 10ms = 60 сек
TIMESYNC_INTERVAL_TICKS equ 6000
; Максимальное допустимое расхождение (100ms в наносекундах / 100)
TIMESYNC_MAX_DRIFT_NS100 equ 1000000
; Флаг: нет событий VMMDev, чисто по таймеру
TIMESYNC_EVENT_MASK equ 0
; Накопительные маски (timesync не требует событий и caps)
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or TIMESYNC_EVENT_MASK )

View File

@@ -0,0 +1,12 @@
; =============================================================================
; Time Sync Structures
; =============================================================================
; VMMDevReq_GetHostTime (request type 10)
; Возвращает время хоста в формате 100-наносекундных интервалов с 01.01.1601
; (Windows FILETIME формат, он же RTTIMESPEC в VBox)
struct VMMDEV_GET_HOST_TIME
header VMMDEV_HEADER
time_low dd ? ; Младшие 32 бита (100ns units since 1601)
time_high dd ? ; Старшие 32 бита
ends

View File

@@ -0,0 +1,63 @@
; =============================================================================
; Модуль : HGCM Async
; Назначение : Ожидание завершения async HGCM запросов
; Файл : hgcm/async.inc
; Версия : 1.0
; Дата : 2025.01.15
; =============================================================================
; hgcm_wait_async
proc hgcm_wait_async uses ebx ecx edx esi, request_ptr:dword
mov esi, [request_ptr]
test esi, esi
jz .bad
mov ecx, HGCM_TIMEOUT_ITERS
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Waiting for async completion (timeout=%d iterations)\n", HGCM_TIMEOUT_ITERS
.wait_loop:
mfence
mov eax, [esi + HGCM_HEADER.flags]
test eax, VBOX_HGCM_REQ_DONE
jnz .completed
test eax, VBOX_HGCM_REQ_CANCELLED
jnz .cancelled
dec ecx
jz .timeout
; короткая задержка
push ecx
mov ecx, 1000
.delay:
pause
loop .delay
pop ecx
jmp .wait_loop
.completed:
mfence
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Async request completed\n"
xor eax, eax
ret
.cancelled:
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Async request cancelled\n"
mov eax, VERR_GENERAL_FAILURE
ret
.timeout:
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] *** ASYNC TIMEOUT ***\n"
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Current flags: 0x%x\n", [esi + HGCM_HEADER.flags]
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Request RC: 0x%x\n", [esi + HGCM_HEADER.header.rc]
mov eax, VERR_TIMEOUT
ret
.bad:
mov eax, VERR_INVALID_POINTER
ret
endp

View File

@@ -0,0 +1,395 @@
; =============================================================================
; Модуль : HGCM Call32 с поддержкой PageList параметров
; Назначение : HGCM вызовы (CALL32) поверх hgcm_send_request
; Автоматическая конвертация LINADDR -> PAGE_LIST (type=10)
; Файл : hgcm/call.inc
; =============================================================================
; -----------------------------------------------------------------------------
; hgcm_call32_pagelist вызов HGCM_CALL32 с auto-pagelist
;
; Вход : client_id, function, parms_ptr, parm_count
; Выход: eax = 0 успех / VERR_*
;
; LINADDR_IN/OUT/INOUT автоматически конвертируются в PAGE_LIST (type=10).
; 32BIT/64BIT передаются как есть.
; После вызова: данные OUT/INOUT уже в памяти гостя (хост писал напрямую
; в физ. страницы). Обновляется поле size в оригинальных параметрах.
; -----------------------------------------------------------------------------
proc hgcm_call32_pagelist uses ebx ecx edx esi edi, client_id:dword, function:dword, parms:dword, parm_count:dword
locals
packet_base dd ?
data_tail dd ? ; текущее смещение хвоста (для PageListInfo)
param_idx dd ?
; Временные переменные для конвертации одного параметра
conv_cbdata dd ? ; размер буфера
conv_vaddr dd ? ; виртуальный адрес буфера
conv_flags dd ? ; флаги направления
conv_off_first dd ? ; смещение в первой странице
conv_cpages dd ? ; количество страниц
conv_page_virt dd ? ; текущий virt addr страницы (для цикла)
endl
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] CALL32: client=0x%x fn=%d cnt=%d\n", \
[client_id], [function], [parm_count]
; --- Проверка: буфер не занят (защита от реентрантности) ---
; lock bts dword [hgcm_pl_busy], 0
; jc .busy
; --- Проверки ---
mov eax, [parm_count]
cmp eax, VMMDEV_MAX_HGCM_PARMS
jbe .count_ok
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] ERROR: parm_count %d > max %d\n", eax, VMMDEV_MAX_HGCM_PARMS
; mov dword [hgcm_pl_busy], 0
mov eax, VERR_INVALID_PARAMETER
ret
.count_ok:
mov edi, [vbox_device.hgcm_call_pl_virt]
test edi, edi
jz .bad
mov [packet_base], edi
; =================================================================
; 1. Заголовок HGCM_CALL
; =================================================================
mov eax, [parm_count]
imul eax, sizeof.HGCM_PARM
add eax, sizeof.HGCM_CALL
hgcm_prepare_header edi, eax, VMMDEV_HGCM_CALL32
mov eax, [client_id]
mov [edi + HGCM_CALL.client_id], eax
mov eax, [function]
mov [edi + HGCM_CALL.function], eax
mov eax, [parm_count]
mov [edi + HGCM_CALL.param_count], eax
; =================================================================
; 2. Копируем параметры из parms[] в пакет
; =================================================================
mov ecx, [parm_count]
test ecx, ecx
jz .send_no_params
push ecx
mov esi, [parms]
lea edi, [edi + sizeof.HGCM_CALL]
imul ecx, sizeof.HGCM_PARM
rep movsb
pop ecx
; =================================================================
; 3. data_tail = начало области для PageListInfo
; =================================================================
mov eax, [parm_count]
imul eax, sizeof.HGCM_PARM
add eax, sizeof.HGCM_CALL
mov [data_tail], eax
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] PageList tail starts at offset %d\n", eax
; =================================================================
; 4. Конвертация LINADDR -> PAGE_LIST
; =================================================================
mov dword [param_idx], 0
.convert_loop:
mov ecx, [param_idx]
cmp ecx, [parm_count]
jae .convert_done
; esi -> текущий параметр в пакете
mov esi, [packet_base]
add esi, sizeof.HGCM_CALL
mov eax, ecx
imul eax, sizeof.HGCM_PARM
add esi, eax
mov eax, [esi + HGCM_PARM.type]
cmp eax, HGCM_PARM_TYPE_LINADDR_IN
je .conv_in
cmp eax, HGCM_PARM_TYPE_LINADDR_OUT
je .conv_out
cmp eax, HGCM_PARM_TYPE_LINADDR_INOUT
je .conv_inout
cmp eax, HGCM_PARM_TYPE_LINADDR
je .conv_inout
; Не LINADDR пропускаем
jmp .next_param
.conv_in:
mov dword [conv_flags], HGCM_PL_FLAG_IN
jmp .do_convert
.conv_out:
mov dword [conv_flags], HGCM_PL_FLAG_OUT
jmp .do_convert
.conv_inout:
mov dword [conv_flags], HGCM_PL_FLAG_BOTH
; ---- Конвертация одного параметра в PAGE_LIST ----
; esi = указатель на HGCM_PARM в пакете (сохраняется)
.do_convert:
; Сохраняем cbData и vaddr в локальные переменные
mov eax, dword [esi + HGCM_PARM.u.LinAddr.size]
mov [conv_cbdata], eax
mov eax, dword [esi + HGCM_PARM.u.LinAddr.offset]
mov [conv_vaddr], eax
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] parm[%d]: LINADDR->PL vaddr=0x%x cb=%d fl=%d\n", \
[param_idx], [conv_vaddr], [conv_cbdata], [conv_flags]
; --- cbData == 0 -> пустой PageList ---
cmp dword [conv_cbdata], 0
je .empty_pagelist
; --- offFirstPage = vaddr & 0xFFF ---
mov eax, [conv_vaddr]
and eax, PAGE_OFFSET_MASK
mov [conv_off_first], eax
; --- cPages = (offFirstPage + cbData + 4095) >> 12 ---
mov eax, [conv_off_first]
add eax, [conv_cbdata]
add eax, PAGE_SIZE - 1
shr eax, 12
mov [conv_cpages], eax
; --- Проверка: хватает ли места в хвосте? ---
; Нужно: HGCM_PLI_APAGES (=8) + conv_cpages * 8
mov eax, [conv_cpages]
shl eax, 3 ; * 8 (размер одного RTGCPHYS64)
add eax, HGCM_PLI_APAGES ; + 8 (заголовок PageListInfo)
add eax, [data_tail]
cmp eax, HGCM_PL_BUF_SIZE
ja .buffer_overflow
; --- Заполняем заголовок HGCMPageListInfo ---
; edi -> PageListInfo в хвосте пакета
mov edi, [packet_base]
add edi, [data_tail]
; flags (direction)
mov eax, [conv_flags]
mov dword [edi + HGCM_PLI_FLAGS], eax
; offFirstPage (word) смещение данных внутри первой страницы
mov eax, [conv_off_first]
mov word [edi + HGCM_PLI_OFF_FIRST_PAGE], ax
; cPages (word)
mov eax, [conv_cpages]
mov word [edi + HGCM_PLI_CPAGES], ax
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] PLI at +%d: fl=%d offFirst=%d cPages=%d\n", \
[data_tail], [conv_flags], [conv_off_first], [conv_cpages]
; --- Заполняем массив aPages[] физическими адресами ---
; Подготовка: стартовый виртуальный адрес страницы
mov eax, [conv_vaddr]
and eax, PAGE_BASE_MASK
mov [conv_page_virt], eax ; virt base первой страницы
xor ecx, ecx ; ecx = индекс страницы
.page_loop:
cmp ecx, [conv_cpages]
jae .pages_done
; Получаем физический адрес текущей страницы
; GetPhysAddr: eax(вход) = virt addr, eax(выход) = phys addr
; Сохраняем всё, что может быть затёрто invoke
push ecx esi edi
mov eax, [conv_page_virt]
invoke GetPhysAddr
mov ebx, eax ; ebx = phys addr
pop edi esi ecx
; Записываем aPages[ecx] как RTGCPHYS64 (8 байт, little-endian)
mov dword [edi + HGCM_PLI_APAGES + ecx*8], ebx ; low 32 bits
mov dword [edi + HGCM_PLI_APAGES + ecx*8 + 4], 0 ; high 32 bits = 0
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] aPages[%d]: virt=0x%x phys=0x%x\n", \
ecx, [conv_page_virt], ebx
; Следующая страница
add dword [conv_page_virt], PAGE_SIZE
inc ecx
jmp .page_loop
.pages_done:
; --- Перезаписываем параметр в пакете как PAGE_LIST ---
; type = 10 (PAGE_LIST)
mov dword [esi + HGCM_PARM.type], HGCM_PARM_TYPE_PAGELIST
; u[0..3] = cbData
mov eax, [conv_cbdata]
mov dword [esi + HGCM_PARM.u], eax
; u[4..7] = смещение к PageListInfo от начала пакета
mov eax, [data_tail]
mov dword [esi + HGCM_PARM.u + 4], eax
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] => type=10 cb=%d offPLI=%d\n", \
[conv_cbdata], [data_tail]
; Сдвигаем data_tail: += HGCM_PLI_APAGES + cPages * 8
mov eax, [conv_cpages]
shl eax, 3 ; * 8
add eax, HGCM_PLI_APAGES ; + 8 (заголовок)
add [data_tail], eax
jmp .next_param
; --- Пустой PageList (cbData == 0) ---
; ВАЖНО: VMMDev проверяет cPages > 0 даже при cbData=0!
; Ставим cPages=1 с фиктивной страницей, чтобы пройти валидацию.
; cbData=0 гарантирует что данные не передаются.
.empty_pagelist:
; Проверка места: 16 байт (8 заголовок + 8 одна фиктивная страница)
mov eax, [data_tail]
add eax, HGCM_PLI_APAGES + 8 ; 8 + 8 = 16
cmp eax, HGCM_PL_BUF_SIZE
ja .buffer_overflow
; Заполняем PageListInfo с 1 фиктивной страницей
mov edi, [packet_base]
add edi, [data_tail]
mov eax, [conv_flags]
mov dword [edi + HGCM_PLI_FLAGS], eax
mov word [edi + HGCM_PLI_OFF_FIRST_PAGE], 0
mov word [edi + HGCM_PLI_CPAGES], 1 ; cPages=1 (не 0!)
mov dword [edi + HGCM_PLI_APAGES], 0 ; dummy phys lo
mov dword [edi + HGCM_PLI_APAGES + 4], 0 ; dummy phys hi
; Записываем параметр
mov dword [esi + HGCM_PARM.type], HGCM_PARM_TYPE_PAGELIST
mov dword [esi + HGCM_PARM.u], 0 ; cbData = 0
mov eax, [data_tail]
mov dword [esi + HGCM_PARM.u + 4], eax ; offset к PLI
; data_tail += 16 (8 заголовок + 8 одна страница)
add dword [data_tail], HGCM_PLI_APAGES + 8
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] parm[%d]: empty pagelist (cPages=1 dummy)\n", [param_idx]
jmp .next_param
.buffer_overflow:
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] ERROR: PageList overflow at param %d\n", [param_idx]
; mov dword [hgcm_pl_busy], 0
mov eax, VERR_TOO_MUCH_DATA
ret
.next_param:
inc dword [param_idx]
jmp .convert_loop
; =================================================================
; 5. Обновляем размер пакета и отправляем
; =================================================================
.convert_done:
mov edi, [packet_base]
mov eax, [data_tail]
mov [edi + VMMDEV_HEADER.size], eax
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] Final packet size: %d bytes\n", eax
; Отправляем
stdcall hgcm_send_request, [packet_base]
; Debug post-send
push eax
mov esi, [packet_base]
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] POST: rc=0x%x result=0x%x\n", \
[esi + HGCM_HEADER.header.rc], [esi + HGCM_HEADER.result]
pop eax
test eax, eax
jnz .send_failed
; =================================================================
; 6. Copyback только размеры и скалярные параметры
; Данные OUT/INOUT уже на месте в памяти гостя.
; =================================================================
mov dword [param_idx], 0
.cb_loop:
mov ecx, [param_idx]
cmp ecx, [parm_count]
jae .cb_done
; esi -> параметр в пакете
mov esi, [packet_base]
add esi, sizeof.HGCM_CALL
mov eax, ecx
imul eax, sizeof.HGCM_PARM
add esi, eax
; edi -> оригинальный параметр в parms[]
mov edi, [parms]
add edi, eax
mov eax, [esi + HGCM_PARM.type]
cmp eax, HGCM_PARM_TYPE_PAGELIST
je .cb_pagelist
cmp eax, HGCM_PARM_TYPE_32BIT
je .cb_32bit
cmp eax, HGCM_PARM_TYPE_64BIT
je .cb_64bit
jmp .cb_next
.cb_32bit:
; 32-bit значение копируем обратно
mov eax, dword [esi + HGCM_PARM.u]
mov dword [edi + HGCM_PARM.u], eax
jmp .cb_next
.cb_64bit:
; 64-bit значение копируем обратно
mov eax, dword [esi + HGCM_PARM.u]
mov dword [edi + HGCM_PARM.u], eax
mov eax, dword [esi + HGCM_PARM.u + 4]
mov dword [edi + HGCM_PARM.u + 4], eax
jmp .cb_next
.cb_pagelist:
; Для PAGE_LIST: хост мог обновить cbData (u[0..3]).
; Копируем обновлённый размер в оригинальный параметр (LinAddr.size).
; Адрес буфера (LinAddr.offset) не меняется.
mov eax, dword [esi + HGCM_PARM.u]
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] cb[%d]: size=%d\n", ecx, eax
jmp .cb_next
.cb_next:
inc dword [param_idx]
jmp .cb_loop
.cb_done:
xor eax, eax
.send_failed:
; mov dword [hgcm_pl_busy], 0
ret
.bad:
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] ERROR: hgcm_call_pl_virt is NULL!\n"
; mov dword [hgcm_pl_busy], 0
mov eax, VERR_INVALID_POINTER
ret
.busy:
DEBUGF 2, "[VBoxGuest] [HGCM] [PageList] BUSY — reentrancy blocked\n"
mov eax, VERR_TRY_AGAIN
ret
.send_no_params:
; Без параметров размер и отправка как в .convert_done
jmp .convert_done
endp

View File

@@ -0,0 +1,5 @@
; HGCM подсистема
include 'hgcm/async.inc'
include 'hgcm/params.inc'
include 'hgcm/hgcm.inc'
include 'hgcm/call.inc'

View File

@@ -0,0 +1,276 @@
; =============================================================================
; Модуль : HGCM Core
; Назначение : HGCM: init packets + connect/disconnect + общий send_request
; Файл : hgcm/hgcm.inc
; =============================================================================
; -----------------------------------------------------------------------------
; Макрос: hgcm_prepare_header
; Назначение: заполнить VMMDevRequestHeader внутри HGCM request
; -----------------------------------------------------------------------------
macro hgcm_prepare_header req_ptr, req_size, req_type
{
mov dword [req_ptr + HGCM_HEADER.header.size], req_size
mov dword [req_ptr + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [req_ptr + HGCM_HEADER.header.request_type], req_type
mov dword [req_ptr + HGCM_HEADER.header.rc], VERR_GENERAL_FAILURE ; 0 ;VERR_NOT_READY
mov dword [req_ptr + HGCM_HEADER.header.reserved1], 0
mov dword [req_ptr + HGCM_HEADER.header.f_requestor], VMMDEV_REQUESTOR_VBOXGUEST
mov dword [req_ptr + HGCM_HEADER.flags], 0
mov dword [req_ptr + HGCM_HEADER.result], 0
}
; -----------------------------------------------------------------------------
; hgcm_init_packets выделить и привязать буферы HGCM (KernelAlloc)
;
; Выделяем:
; 1 страница (4096) для connect + disconnect (помещаются вместе)
; 1 страница (4096) для hgcm_call_s (embedded)
; N страниц для hgcm_call_pl_s (pagelist)
; -----------------------------------------------------------------------------
proc hgcm_init_packets uses ebx
; --- Страница для connect + disconnect ---
invoke KernelAlloc, PAGE_SIZE
test eax, eax
jz .alloc_fail
; connect = начало страницы
mov [vbox_device.hgcm_connect_virt], eax
invoke GetPhysAddr
mov [vbox_device.hgcm_connect_phys], eax
; disconnect = connect_virt + sizeof.HGCM_CONNECT (выровнено на 4)
mov eax, [vbox_device.hgcm_connect_virt]
add eax, ((sizeof.HGCM_CONNECT + 3) and (not 3))
mov [vbox_device.hgcm_disconnect_virt], eax
invoke GetPhysAddr
mov [vbox_device.hgcm_disconnect_phys], eax
; --- Страница для embedded call ---
invoke KernelAlloc, PAGE_SIZE
test eax, eax
jz .alloc_fail_free1
mov [vbox_device.hgcm_call_virt], eax
invoke GetPhysAddr
mov [vbox_device.hgcm_call_phys], eax
; --- Буфер для pagelist call
invoke KernelAlloc, HGCM_PL_BUF_SIZE
test eax, eax
jz .alloc_fail_free2
mov [vbox_device.hgcm_call_pl_virt], eax
invoke GetPhysAddr
mov [vbox_device.hgcm_call_pl_phys], eax
DEBUGF 2, "[VBoxGuest] [HGCM] Packets allocated: connect=0x%x call=0x%x pl=0x%x\n", \
[vbox_device.hgcm_connect_virt], [vbox_device.hgcm_call_virt], [vbox_device.hgcm_call_pl_virt]
xor eax, eax
ret
.alloc_fail_free2:
invoke KernelFree, [vbox_device.hgcm_call_virt]
.alloc_fail_free1:
invoke KernelFree, [vbox_device.hgcm_connect_virt]
.alloc_fail:
DEBUGF 2, "[VBoxGuest] [HGCM] ERROR: KernelAlloc failed for HGCM packets\n"
mov eax, VERR_NO_MEMORY
ret
endp
; -----------------------------------------------------------------------------
; hgcm_free_packets освободить буферы HGCM
; -----------------------------------------------------------------------------
proc hgcm_free_packets
; Pagelist call buffer
cmp dword [vbox_device.hgcm_call_pl_virt], 0
je @f
invoke KernelFree, [vbox_device.hgcm_call_pl_virt]
mov dword [vbox_device.hgcm_call_pl_virt], 0
@@:
; Embedded call buffer
cmp dword [vbox_device.hgcm_call_virt], 0
je @f
invoke KernelFree, [vbox_device.hgcm_call_virt]
mov dword [vbox_device.hgcm_call_virt], 0
@@:
; Connect+disconnect page
cmp dword [vbox_device.hgcm_connect_virt], 0
je @f
invoke KernelFree, [vbox_device.hgcm_connect_virt]
mov dword [vbox_device.hgcm_connect_virt], 0
mov dword [vbox_device.hgcm_disconnect_virt], 0
@@:
ret
endp
; -----------------------------------------------------------------------------
; hgcm_init инициализация HGCM подсистемы
; -----------------------------------------------------------------------------
proc hgcm_init
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Initializing HGCM subsystem\n"
; Инициализируем пакеты
call hgcm_init_packets
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Initialization complete\n"
ret
endp
; hgcm_send_request отправить HGCM request через VMMDev + обработать ASYNC
proc hgcm_send_request uses ebx ecx edx esi edi, request_ptr:dword
mov esi, [request_ptr]
test esi, esi
jz .bad_ptr
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Sending request type=0x%x\n", [esi + HGCM_HEADER.header.request_type]
; phys адрес запроса
mov eax, esi
invoke GetPhysAddr
test eax, eax
jz .phys_failed
mov edx, eax
; отправка VMMDev (submit-only, хост обновляет пакет через MMIO)
stdcall vmmdev_send_request, edx
; транспортный rc
mov eax, [esi + HGCM_HEADER.header.rc]
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Transport RC: 0x%x\n", eax
cmp eax, VINF_HGCM_ASYNC_EXECUTE
je .async
; если rc < 0 ошибка транспорта
test eax, eax
js .transport_error
; sync ok -> проверяем service result
jmp .check_service
.async:
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Request is async, waiting...\n"
stdcall hgcm_wait_async, esi
test eax, eax
jnz .async_failed
; после async: транспортный rc
mov eax, [esi + HGCM_HEADER.header.rc]
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Post-async transport RC: 0x%x\n", eax
test eax, eax
js .transport_error_after_async
.check_service:
; результат сервиса
mov eax, [esi + HGCM_HEADER.result]
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Service result: 0x%x\n", eax
test eax, eax
js .service_error
xor eax, eax
ret
.bad_ptr:
mov eax, VERR_INVALID_POINTER
ret
.phys_failed:
mov eax, VERR_INVALID_POINTER
ret
.transport_error:
ret
.transport_error_after_async:
ret
.service_error:
ret
.async_failed:
ret
endp
; hgcm_connect подключение к HGCM сервису
proc hgcm_connect uses ebx ecx edx esi edi, service_name:dword
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Connecting to service: %s\n", [service_name]
mov edi, [vbox_device.hgcm_connect_virt]
test edi, edi
jz .failed
; header
hgcm_prepare_header edi, sizeof.HGCM_CONNECT, VMMDEV_HGCM_CONNECT
; location
mov dword [edi + HGCM_CONNECT.location_type], HGCM_LOC_TYPE_PREDEFINED
; очистить service_name буфер
push edi
lea edi, [edi + HGCM_CONNECT.service_name]
xor eax, eax
mov ecx, HGCM_SERVICE_NAME_MAX / 4
rep stosd
pop edi
; копировать ASCII имя сервиса
mov esi, [service_name]
push edi
lea edi, [edi + HGCM_CONNECT.service_name]
mov ecx, HGCM_SERVICE_NAME_MAX - 1
.copy_name:
lodsb
test al, al
jz .name_done
stosb
loop .copy_name
.name_done:
xor al, al
stosb
pop edi
; client_id = 0
mov dword [edi + HGCM_CONNECT.client_id], 0
; отправить
stdcall hgcm_send_request, [vbox_device.hgcm_connect_virt]
test eax, eax
jnz .failed
mov eax, [edi + HGCM_CONNECT.client_id]
test eax, eax
jz .failed
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Connected, client_id=0x%x\n", eax
ret
.failed:
DEBUGF 2, "[VBoxGuest] [HGCM] Connect failed\n"
xor eax, eax
ret
endp
; hgcm_disconnect отключение клиента HGCM
proc hgcm_disconnect uses edi, client_id:dword
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Disconnecting client: 0x%x\n", [client_id]
mov edi, [vbox_device.hgcm_disconnect_virt]
test edi, edi
jz .bad
hgcm_prepare_header edi, sizeof.HGCM_DISCONNECT, VMMDEV_HGCM_DISCONNECT
mov eax, [client_id]
mov [edi + HGCM_DISCONNECT.client_id], eax
stdcall hgcm_send_request, [vbox_device.hgcm_disconnect_virt]
ret
.bad:
mov eax, VERR_INVALID_POINTER
ret
endp

View File

@@ -0,0 +1,78 @@
; =============================================================================
; Модуль : HGCM Params
; Назначение : Формирование параметров HGCM_PARM
; Файл : hgcm/params.inc
; Версия : 1.1
; Дата : 2025.01.15
; =============================================================================
; hgcm_param_32 заполнить 32-битный параметр
;
; Вход : p указатель на HGCM_PARM, value значение
proc hgcm_param_32 uses edi, p:dword, value:dword
mov edi, [p]
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov eax, [value]
mov dword [edi + HGCM_PARM.u.value32], eax
ret
endp
; -----------------------------------------------------------------------------
; hgcm_param_64 заполнить 64-битный параметр
;
; Вход : p указатель на HGCM_PARM, lo/hi младшая/старшая часть
; -----------------------------------------------------------------------------
proc hgcm_param_64 uses edi, p:dword, lo:dword, hi:dword
mov edi, [p]
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_64BIT
mov eax, [lo]
mov dword [edi + HGCM_PARM.u.value64_lo], eax
mov eax, [hi]
mov dword [edi + HGCM_PARM.u.value64_hi], eax
ret
endp
; -----------------------------------------------------------------------------
; hgcm_param_linaddr_in линейный буфер IN (гость хост)
;
; Вход : p указатель на HGCM_PARM, addr адрес буфера, size размер
; -----------------------------------------------------------------------------
proc hgcm_param_linaddr_in uses edi, p:dword, addr:dword, size:dword
mov edi, [p]
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_IN
mov eax, [size]
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
mov eax, [addr]
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
ret
endp
; -----------------------------------------------------------------------------
; hgcm_param_linaddr_out линейный буфер OUT (хост гость)
;
; Вход : p указатель на HGCM_PARM, addr адрес буфера, size размер
; -----------------------------------------------------------------------------
proc hgcm_param_linaddr_out uses edi, p:dword, addr:dword, size:dword
mov edi, [p]
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_OUT
mov eax, [size]
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
mov eax, [addr]
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
ret
endp
; -----------------------------------------------------------------------------
; hgcm_param_linaddr_inout линейный буфер INOUT (двунаправленный)
;
; Вход : p указатель на HGCM_PARM, addr адрес буфера, size размер
; -----------------------------------------------------------------------------
proc hgcm_param_linaddr_inout uses edi, p:dword, addr:dword, size:dword
mov edi, [p]
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_INOUT
mov eax, [size]
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
mov eax, [addr]
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
ret
endp

View File

@@ -0,0 +1,220 @@
; =============================================================================
; Модуль : VBox Shared Clipboard Service
; Файл : services/clipboard/clipboard.inc
;
; Архитектура : Async state machine + IOCTL ABI
; clipboard_init HGCM connect + аллокация listener пакета
; clipboard_enable отправка первого MSG_OLD_GET_WAIT (async)
; clipboard_on_tick проверка завершения async запроса + resubmit
;
; Данные хоста и гостя передаются через IOCTL (приложение):
; CLIP_STATUS (10) статус (formats, host_new)
; CLIP_READ (11) чтение данных хоста (HGCM DATA_READ)
; CLIP_WRITE (12) запись данных гостя (буферизация + HGCM REPORT_FORMATS)
;
; Источник протокола: VBox 7.2.6
; include/VBox/HostServices/VBoxClipboardSvc.h
; =============================================================================
svc_clipboard_name db "CLIPBOARD", 0
align 4
sz_clipboard_hgcm_service db 'VBoxSharedClipboard', 0
align 4
clip_state CLIPBOARD_STATE
; clipboard_init Подключиться к HGCM + выделить listener пакет
proc clipboard_init uses ebx
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Initializing...\n"
mov eax, [vbox_device.hgcm_connect_virt]
test eax, eax
jz .hgcm_not_ready
cmp dword [clip_state.connected], 1
je .already_connected
; --- Подключиться к HGCM ---
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Connecting to '%s'...\n", sz_clipboard_hgcm_service
stdcall hgcm_connect, sz_clipboard_hgcm_service
test eax, eax
jz .connect_failed
mov [clip_state.client_id], eax
mov dword [clip_state.connected], 1
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
mov dword [clip_state.error_count], 0
mov dword [clip_state.formats_host], 0
mov dword [clip_state.formats_guest], 0
mov dword [clip_state.has_data], 0
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Connected, client_id=%d\n", eax
; --- Выделить listener пакет (или переиспользовать существующий) ---
; При fire-and-forget disconnect пакет не освобождается (хост может
; ещё DMA-писать DONE flag), поэтому переиспользуем при re-enable.
cmp dword [clip_state.listen_pkt_virt], 0
jne .pkt_ready
invoke KernelAlloc, 4096
test eax, eax
jz .alloc_failed
mov [clip_state.listen_pkt_virt], eax
invoke GetPhysAddr
mov [clip_state.listen_pkt_phys], eax
.pkt_ready:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener pkt: virt=0x%x, phys=0x%x\n", \
[clip_state.listen_pkt_virt], [clip_state.listen_pkt_phys]
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Initialized OK\n"
xor eax, eax
ret
.alloc_failed:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] KernelAlloc failed for listener pkt\n"
stdcall hgcm_disconnect, [clip_state.client_id]
mov dword [clip_state.connected], 0
mov eax, VERR_NO_MEMORY
ret
.connect_failed:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] HGCM connect failed\n"
mov eax, VERR_HGCM_SERVICE_NOT_FOUND
ret
.hgcm_not_ready:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] HGCM not ready\n"
mov eax, VERR_NOT_READY
ret
.already_connected:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Already connected\n"
xor eax, eax
ret
endp
; clipboard_enable Отправить первый MSG_OLD_GET_WAIT
proc clipboard_enable
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enabling (async listener)...\n"
cmp dword [clip_state.connected], 0
jne .connected
; После disable HGCM отключен переподключиться
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Not connected, re-initializing...\n"
call clipboard_init
test eax, eax
jnz .init_failed
.connected:
; Отправить первый listener запрос
call clipboard_listener_submit
test eax, eax
jnz .submit_failed
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enabled OK\n"
xor eax, eax
ret
.init_failed:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enable: re-init failed (0x%x)\n", eax
ret
.submit_failed:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enable: first submit failed (0x%x)\n", eax
xor eax, eax
ret
endp
; clipboard_on_event Вызывается из IRQ (dispatcher_dispatch)
; ПРИМЕЧАНИЕ: IRQ обработка полностью делегирована on_tick,
; поэтому здесь просто возвращаем (событие уже обработано в tick)
proc clipboard_on_event
xor eax, eax
ret
endp
; clipboard_on_tick Вызывается из таймера каждые 100ms
proc clipboard_on_tick
cmp dword [clip_state.connected], 0
je .done
cmp dword [clip_state.error_count], CLIP_MAX_ERRORS
jae .done
mov eax, [clip_state.listen_state]
cmp eax, CLIP_LISTEN_SUBMITTED
je .check
; IDLE отправить новый запрос
call clipboard_listener_submit
jmp .done
.check:
call clipboard_listener_check
.done:
ret
endp
; clipboard_disable Остановить listener, disconnect HGCM
proc clipboard_disable uses edi
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Disabling...\n"
cmp dword [clip_state.connected], 0
je .done
; Если listener pending disconnect fire-and-forget (без busy-wait).
; При shutdown VBox не может быстро обработать disconnect из-за pending
; MSG_OLD_GET_WAIT, и hgcm_send_request зависает в hgcm_wait_async
; на ~30-60 сек (HGCM_TIMEOUT_MS = 500000 итераций busy-loop).
cmp dword [clip_state.listen_state], CLIP_LISTEN_SUBMITTED
je .fast_disconnect
; Нет pending запроса обычный disconnect (sync, быстрый)
stdcall hgcm_disconnect, [clip_state.client_id]
jmp .cleanup
.fast_disconnect:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener pending — fire-and-forget disconnect\n"
mov edi, [vbox_device.hgcm_disconnect_virt]
test edi, edi
jz .cleanup
hgcm_prepare_header edi, sizeof.HGCM_DISCONNECT, VMMDEV_HGCM_DISCONNECT
mov eax, [clip_state.client_id]
mov [edi + HGCM_DISCONNECT.client_id], eax
; Отправить через VMMDev напрямую (non-blocking out dx, eax)
; НЕ вызываем hgcm_send_request чтобы избежать hgcm_wait_async
stdcall vmmdev_send_request, [vbox_device.hgcm_disconnect_phys]
.cleanup:
; НЕ освобождаем listener пакет хост может ещё DMA-писать DONE flag
; в эту страницу. Пакет будет переиспользован при следующем enable.
; Освободить буфер guest→host данных
cmp dword [clip_guest_buf_ptr], 0
je .no_guest_buf
invoke KernelFree, [clip_guest_buf_ptr]
mov dword [clip_guest_buf_ptr], 0
mov dword [clip_guest_buf_size], 0
.no_guest_buf:
mov dword [clip_state.connected], 0
mov dword [clip_state.client_id], 0
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
mov dword [clip_host_new], 0
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Disabled\n"
.done:
xor eax, eax
ret
endp
include 'clipboard_listener.inc'
REGISTER_SERVICE svc_clipboard_name, CLIPBOARD_EVENT_MASK, CLIPBOARD_CAPS_MASK, \
clipboard_init, clipboard_enable, clipboard_disable, clipboard_on_event, clipboard_on_tick, AUTOSTART_CLIPBOARD

View File

@@ -0,0 +1,614 @@
; =============================================================================
; Clipboard Listener + IOCTL handlers
;
; Файл: services/clipboard/clipboard_listener.inc
;
; Listener: async MSG_OLD_GET_WAIT для получения сообщений от хоста
; IOCTL: clip_ioctl_status, clip_ioctl_read, clip_ioctl_write
; =============================================================================
; Размер пакета: HGCM_CALL(44) + 2 * HGCM_PARM(12) = 68
CLIP_LISTEN_PKT_SIZE = sizeof.HGCM_CALL + (VBOX_SHCL_CPARMS_MSG_OLD_GET_WAIT * sizeof.HGCM_PARM)
; Смещения параметров от начала пакета
CLIP_LISTEN_PARM0 = sizeof.HGCM_CALL ; offset 44: msg_type
CLIP_LISTEN_PARM1 = sizeof.HGCM_CALL + sizeof.HGCM_PARM ; offset 56: formats
; Начальный размер буфера для HGCM DATA_READ (retry с большим если не хватит)
CLIP_INITIAL_BUF_SIZE = 65536 ; 64KB
; Максимум символов для debug вывода
CLIP_DEBUG_MAX_CHARS = 200
; Статический буфер для cp866-вывода (UTF-16LE cp866)
align 4
clip_ascii_buf rb CLIP_DEBUG_MAX_CHARS + 4
; Host clipboard state (set by async listener, read by IOCTL)
align 4
clip_host_new dd 0 ; 1 = host clipboard changed since last CLIP_READ
; Guest host buffered data (set by CLIP_WRITE, sent on host READ_DATA)
align 4
clip_guest_buf_ptr dd 0 ; KernelAlloc'd buffer, or 0
clip_guest_buf_size dd 0 ; data size in bytes
clip_guest_buf_fmt dd 0 ; VBOX_SHCL_FMT_*
; clipboard_listener_submit — Отправить MSG_OLD_GET_WAIT (async)
proc clipboard_listener_submit uses ebx edx edi
mov edi, [clip_state.listen_pkt_virt]
test edi, edi
jz .bad_ptr
; --- Заполнить HGCM_CALL заголовок ---
hgcm_prepare_header edi, CLIP_LISTEN_PKT_SIZE, VMMDEV_HGCM_CALL32
mov eax, [clip_state.client_id]
mov [edi + HGCM_CALL.client_id], eax
mov dword [edi + HGCM_CALL.function], VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT
mov dword [edi + HGCM_CALL.param_count], VBOX_SHCL_CPARMS_MSG_OLD_GET_WAIT
; --- parm[0]: msg_type (32BIT, OUT) ---
mov dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.u.value32], 0
mov dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.u.value32 + 4], 0
; --- parm[1]: formats (32BIT, OUT) ---
mov dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.u.value32], 0
mov dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.u.value32 + 4], 0
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Submitting MSG_OLD_GET_WAIT (async)...\n"
stdcall vmmdev_send_request, [clip_state.listen_pkt_phys]
mov eax, [edi + HGCM_HEADER.header.rc]
cmp eax, VINF_HGCM_ASYNC_EXECUTE
je .async_ok
test eax, eax
js .error
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] MSG_OLD_GET_WAIT completed sync (rc=0x%x)\n", eax
mov dword [clip_state.listen_state], CLIP_LISTEN_SUBMITTED
xor eax, eax
ret
.async_ok:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] MSG_OLD_GET_WAIT submitted (async)\n"
mov dword [clip_state.listen_state], CLIP_LISTEN_SUBMITTED
xor eax, eax
ret
.error:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] MSG_OLD_GET_WAIT submit failed: rc=0x%x\n", eax
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
ret
.bad_ptr:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener packet not allocated!\n"
mov eax, VERR_INVALID_POINTER
ret
endp
; clipboard_listener_check — Проверить завершение async запроса
proc clipboard_listener_check uses ebx edx edi
mov edi, [clip_state.listen_pkt_virt]
mfence
mov eax, [edi + HGCM_HEADER.flags]
test eax, VBOX_HGCM_REQ_DONE
jz .not_done
mfence
mov eax, [edi + HGCM_HEADER.header.rc]
test eax, eax
js .transport_error
mov eax, [edi + HGCM_HEADER.result]
test eax, eax
js .service_error
mov eax, dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.u.value32]
mov edx, dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.u.value32]
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Got message: type=%d, formats=0x%x\n", eax, edx
push eax edx
stdcall clipboard_listener_dispatch, eax, edx
pop edx eax
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
mov dword [clip_state.error_count], 0
mov eax, 1
ret
.transport_error:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener transport error: rc=0x%x\n", eax
jmp .handle_error
.service_error:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener service error: result=0x%x\n", eax
.handle_error:
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
inc dword [clip_state.error_count]
mov eax, 1
ret
.not_done:
xor eax, eax
ret
endp
; clipboard_listener_dispatch — Обработать сообщение хоста
proc clipboard_listener_dispatch stdcall, msg_type:dword, formats:dword
mov eax, [msg_type]
cmp eax, VBOX_SHCL_HOST_MSG_FORMATS_REPORT
je .formats_report
cmp eax, VBOX_SHCL_HOST_MSG_READ_DATA
je .read_data
cmp eax, VBOX_SHCL_HOST_MSG_QUIT
je .quit
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Unknown msg_type=%d\n", eax
ret
.formats_report:
; Хост сообщает что у него новые данные в клипборде
mov eax, [formats]
mov [clip_state.formats_host], eax
mov dword [clip_host_new], 1
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Host formats: 0x%x (flagged for app)\n", eax
ret
.read_data:
; Хост хочет прочитать данные гостя — отправляем буферизованные
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Host requests READ_DATA (formats=0x%x, buf=%d bytes)\n", \
[formats], [clip_guest_buf_size]
cmp dword [clip_guest_buf_ptr], 0
je .no_guest_data
stdcall clipboard_respond_read_data
ret
.no_guest_data:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] READ_DATA: no guest data buffered!\n"
ret
.quit:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Host sent QUIT\n"
mov dword [clip_state.connected], 0
ret
endp
; clipboard_respond_read_data — Отправить буферизованные данные хосту
proc clipboard_respond_read_data stdcall uses ebx ecx edx esi edi
locals
parms rb sizeof.HGCM_PARM * 2
endl
; parm[0]: format (32BIT, IN)
lea edi, [parms]
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov eax, [clip_guest_buf_fmt]
mov dword [edi + HGCM_PARM.u.value32], eax
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
; parm[1]: data (LINADDR_IN)
add edi, sizeof.HGCM_PARM
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_IN
mov eax, [clip_guest_buf_size]
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
mov eax, [clip_guest_buf_ptr]
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Sending %d bytes to host (fmt=0x%x)\n", \
[clip_guest_buf_size], [clip_guest_buf_fmt]
lea eax, [parms]
stdcall hgcm_call32_pagelist, [clip_state.client_id], \
VBOX_SHCL_GUEST_FN_DATA_WRITE, eax, VBOX_SHCL_CPARMS_DATA_WRITE_OLD
test eax, eax
jnz .error
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Guest data sent to host OK\n"
ret
.error:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] DATA_WRITE FAILED: 0x%x\n", eax
ret
endp
; clipboard_report_formats — Сообщить хосту о форматах гостевого clipboard
proc clipboard_report_formats stdcall uses ebx ecx edx esi edi, formats:dword
locals
parms rb sizeof.HGCM_PARM
endl
lea edi, [parms]
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov eax, [formats]
mov dword [edi + HGCM_PARM.u.value32], eax
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
lea eax, [parms]
stdcall hgcm_call32_pagelist, [clip_state.client_id], \
VBOX_SHCL_GUEST_FN_REPORT_FORMATS, eax, VBOX_SHCL_CPARMS_REPORT_FORMATS
test eax, eax
jnz .error
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Reported formats 0x%x to host\n", [formats]
ret
.error:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Report formats FAILED: 0x%x\n", eax
ret
endp
; clip_ioctl_status — IOCTL 10: Статус clipboard
proc clip_ioctl_status stdcall uses ebx, ioctl_ptr:dword
mov ebx, [ioctl_ptr]
cmp [ebx + IOCTL.out_size], 12
jb .fail
mov eax, [ebx + IOCTL.output]
mov ecx, [clip_state.connected]
mov [eax], ecx
mov ecx, [clip_state.formats_host]
mov [eax + 4], ecx
mov ecx, [clip_host_new]
mov [eax + 8], ecx
xor eax, eax
ret
.fail:
or eax, -1
ret
endp
; clip_ioctl_read — IOCTL 11: Прочитать clipboard хоста
proc clip_ioctl_read stdcall uses ebx ecx edx esi edi, ioctl_ptr:dword
locals
parms rb sizeof.HGCM_PARM * 3
kern_buf dd ?
kern_size dd ?
req_format dd ?
endl
mov ebx, [ioctl_ptr]
; Validate input/output
cmp [ebx + IOCTL.inp_size], 4
jb .fail
cmp [ebx + IOCTL.out_size], 4
jb .fail
cmp dword [clip_state.connected], 0
je .fail
; Сбросить host_new СРАЗУ — чтобы при ошибке HGCM приложение
; не повторяло CLIP_READ бесконечно (retry storm).
; Новое изменение хоста снова выставит host_new=1 через listener.
mov dword [clip_host_new], 0
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: out_size=%d\n", [ebx + IOCTL.out_size]
; Get requested format
mov eax, [ebx + IOCTL.input]
mov eax, [eax]
mov [req_format], eax
; Calculate kernel buffer size: max(out_size - 4, CLIP_INITIAL_BUF_SIZE)
mov ecx, [ebx + IOCTL.out_size]
sub ecx, 4
cmp ecx, CLIP_INITIAL_BUF_SIZE
jae .size_ok
mov ecx, CLIP_INITIAL_BUF_SIZE
.size_ok:
; Round up to page
add ecx, (PAGE_SIZE - 1)
and ecx, PAGE_BASE_MASK
mov [kern_size], ecx
; Allocate kernel buffer
invoke KernelAlloc, ecx
test eax, eax
jz .no_mem
mov [kern_buf], eax
.do_read:
; --- Заполнить HGCM DATA_READ параметры ---
lea edi, [parms]
; parm[0]: format (32BIT, IN)
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov eax, [req_format]
mov dword [edi + HGCM_PARM.u.value32], eax
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
; parm[1]: buffer (LINADDR_OUT)
add edi, sizeof.HGCM_PARM
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_OUT
mov eax, [kern_size]
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
mov eax, [kern_buf]
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
; parm[2]: size (32BIT, OUT)
add edi, sizeof.HGCM_PARM
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov dword [edi + HGCM_PARM.u.value32], 0
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
; --- Вызвать HGCM DATA_READ ---
lea eax, [parms]
stdcall hgcm_call32_pagelist, [clip_state.client_id], \
VBOX_SHCL_GUEST_FN_DATA_READ, eax, VBOX_SHCL_CPARMS_DATA_READ
; Прочитать actual_size из parm[2]
lea edi, [parms]
mov ecx, dword [edi + 2 * sizeof.HGCM_PARM + HGCM_PARM.u.value32]
; Проверить результат HGCM
test eax, eax
jnz .check_overflow
; Успех — данные прочитаны
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: OK, %d bytes (fmt=0x%x)\n", ecx, [req_format]
; Записать actual_size в output[0]
mov ebx, [ioctl_ptr]
mov edi, [ebx + IOCTL.output]
mov [edi], ecx
; Хватает ли места в output для данных?
mov eax, [ebx + IOCTL.out_size]
sub eax, 4
cmp eax, ecx
jb .app_overflow
; Скопировать данные из kern_buf в output + 4
push ecx
mov esi, [kern_buf]
add edi, 4
cld
rep movsb
pop ecx
; Debug: вывести текст если это UNICODETEXT
cmp dword [req_format], VBOX_SHCL_FMT_UNICODETEXT
jne .read_ok
cmp ecx, 4
jb .read_ok
stdcall clipboard_utf16_to_debug, [kern_buf], ecx
.read_ok:
invoke KernelFree, [kern_buf]
xor eax, eax ; success
ret
.app_overflow:
; Буфер приложения мал — actual_size записан, данных нет
invoke KernelFree, [kern_buf]
mov eax, 1 ; buffer overflow
ret
.check_overflow:
; HGCM вернул ошибку — если actual_size > kern_size, это overflow
; Попробовать с большим буфером
cmp ecx, [kern_size]
jbe .hgcm_error ; не overflow, реальная ошибка
; actual_size > kern_size — нужен буфер побольше
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: overflow, need %d bytes\n", ecx
invoke KernelFree, [kern_buf]
; Выделить буфер нужного размера (round up to page)
add ecx, (PAGE_SIZE - 1)
and ecx, PAGE_BASE_MASK
mov [kern_size], ecx
invoke KernelAlloc, ecx
test eax, eax
jz .no_mem
mov [kern_buf], eax
jmp .do_read ; retry
.hgcm_error:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: HGCM error 0x%x\n", eax
; Записать actual_size если возможно
mov ebx, [ioctl_ptr]
cmp [ebx + IOCTL.out_size], 4
jb .hgcm_err_ret
mov edi, [ebx + IOCTL.output]
mov [edi], ecx
.hgcm_err_ret:
invoke KernelFree, [kern_buf]
or eax, -1
ret
.no_mem:
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: KernelAlloc failed\n"
or eax, -1
ret
.fail:
or eax, -1
ret
endp
; clip_ioctl_write — IOCTL 12: Записать данные в clipboard хоста
proc clip_ioctl_write stdcall uses ebx ecx edx esi edi, ioctl_ptr:dword
mov ebx, [ioctl_ptr]
cmp [ebx + IOCTL.inp_size], 8 ; минимум: dd format + хотя бы 4 байта данных
jb .fail
cmp dword [clip_state.connected], 0
je .fail
; Прочитать format и data size
mov esi, [ebx + IOCTL.input]
mov ecx, [esi] ; format
mov edx, [ebx + IOCTL.inp_size]
sub edx, 4 ; data_size = inp_size - 4
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_WRITE: %d bytes, fmt=0x%x\n", edx, ecx
; Освободить предыдущий буфер
cmp dword [clip_guest_buf_ptr], 0
je .no_old
push ecx edx esi
invoke KernelFree, [clip_guest_buf_ptr]
mov dword [clip_guest_buf_ptr], 0
pop esi edx ecx
.no_old:
; Выделить новый буфер (round up to page, min 4096)
push ecx edx esi
mov eax, edx
add eax, (PAGE_SIZE - 1)
and eax, PAGE_BASE_MASK
cmp eax, PAGE_SIZE
jae .alloc_go
mov eax, PAGE_SIZE
.alloc_go:
invoke KernelAlloc, eax
test eax, eax
jz .no_mem
mov [clip_guest_buf_ptr], eax
pop esi edx ecx
mov [clip_guest_buf_size], edx
mov [clip_guest_buf_fmt], ecx
; Скопировать данные из input + 4 в ядерный буфер
add esi, 4 ; skip format field
mov edi, [clip_guest_buf_ptr]
mov ecx, edx
cld
rep movsb
; Сообщить хосту что у гостя есть данные
stdcall clipboard_report_formats, [clip_guest_buf_fmt]
xor eax, eax
ret
.no_mem:
pop esi edx ecx
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_WRITE: KernelAlloc failed\n"
or eax, -1
ret
.fail:
or eax, -1
ret
endp
; clipboard_utf16_to_debug — Конвертировать UTF-16LE -> cp866 и вывести
proc clipboard_utf16_to_debug stdcall uses ebx ecx edx esi edi, buf_ptr:dword, buf_size:dword
mov esi, [buf_ptr]
mov edi, clip_ascii_buf
mov ecx, [buf_size]
shr ecx, 1 ; количество UTF-16 символов (size / 2)
; Ограничить до CLIP_DEBUG_MAX_CHARS - 1
cmp ecx, CLIP_DEBUG_MAX_CHARS - 1
jbe .len_ok
mov ecx, CLIP_DEBUG_MAX_CHARS - 1
.len_ok:
test ecx, ecx
jz .done_empty
.loop:
lodsw ; ax = UTF-16LE char (si += 2)
; Null terminator?
test ax, ax
jz .done
; --- ASCII range (< 0x80) ---
cmp ax, 0x80
jb .ascii
; --- Cyrillic: U+0410..U+044F → cp866 ---
cmp ax, 0x0410
jb .check_yo
cmp ax, 0x0440
jb .cyr_80 ; U+0410..U+043F → cp866 0x80..0xAF
cmp ax, 0x0450
jb .cyr_e0 ; U+0440..U+044F → cp866 0xE0..0xEF
jmp .check_yo
.cyr_80:
sub ax, 0x0410
add al, 0x80
jmp .store
.cyr_e0:
sub ax, 0x0440
add al, 0xE0
jmp .store
.check_yo:
cmp ax, 0x0401 ; Ё → cp866 0xF0
je .yo_upper
cmp ax, 0x0451 ; ё → cp866 0xF1
je .yo_lower
; Unknown non-ASCII → '?'
mov al, '?'
jmp .store
.yo_upper:
mov al, 0xF0
jmp .store
.yo_lower:
mov al, 0xF1
jmp .store
.ascii:
; Контрольные символы (< 0x20) кроме \n, \r, \t → пропускаем
cmp al, 0x20
jae .store
cmp al, 0x0A
je .store
cmp al, 0x0D
je .store
cmp al, 0x09
je .store
jmp .skip
.store:
stosb
.skip:
dec ecx
jnz .loop
.done:
xor al, al
stosb
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Text: '%s'\n", clip_ascii_buf
ret
.done_empty:
mov byte [clip_ascii_buf], 0
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Text: (empty)\n"
ret
endp

View File

@@ -0,0 +1,206 @@
; =============================================================================
; Модуль : VMMDev Display Service (Auto-resize)
; Назначение : Автоматическое изменение разрешения экрана при resize окна VBox
; Файл : services/display/display.inc
;
; Логика задержки (паттерн heartbeat):
; Event запомнить время, поставить флаг pending
; Tick если pending и прошло 2 секунды display_change
; Каждое новое событие сбрасывает таймер (debounce)
; =============================================================================
svc_display_name db "DISPLAY", 0
; --- Display Data ---
align 4
vbox_display_enabled dd 0
kos_display_ptr dd 0
; Отложенное обновление (debounce)
vbox_display_pending dd 0 ; 1 = есть отложенный запрос
vbox_display_event_time dd 0 ; timer_ticks момента последнего события
DISPLAY_DELAY_TICKS equ 50 ; 500ms (50 * 10ms)
; Статический буфер для запроса GetDisplayChangeRequest2
align 4
vmmdev_display_change_s VMMDEV_GET_DISPLAY_CHANGE_REQUEST2 \
<sizeof.VMMDEV_GET_DISPLAY_CHANGE_REQUEST2, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_GET_DISPLAY_CHANGE_2, \
0, 0, VMMDEV_REQUESTOR_KERNEL>, \
0, 0, 0, 1, 0 ; x_res, y_res, bpp, event_ack=1, display=0
; --- BGA Macros ---
macro bga_write index_val, data_val {
mov eax, index_val
mov dx, VBE_DISPI_IOPORT_INDEX
out dx, ax
mov eax, data_val
mov dx, VBE_DISPI_IOPORT_DATA
out dx, ax
}
macro bga_set_mode w, h, bpp {
bga_write VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED
bga_write VBE_DISPI_INDEX_XRES, w
bga_write VBE_DISPI_INDEX_YRES, h
bga_write VBE_DISPI_INDEX_BPP, bpp
bga_write VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED or VBE_DISPI_LFB_ENABLED
}
; vmmdev_display_init Инициализация подсистемы дисплея
proc vmmdev_display_init
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Initializing...\n"
; Получить указатель на DISPLAY структуру ядра KolibriOS
invoke GetDisplay
mov [kos_display_ptr], eax
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] KOS display struct: 0x%x\n", eax
; Получить физический адрес буфера запроса
mov eax, vmmdev_display_change_s
mov [vbox_device.display_virt], eax
invoke GetPhysAddr
mov [vbox_device.display_phys], eax
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Buffer: virt=0x%x, phys=0x%x\n", \
vmmdev_display_change_s, eax
mov dword [vbox_display_enabled], 1
; Сразу обработать текущее разрешение (без задержки)
call display_change
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Initialized OK\n"
xor eax, eax
ret
endp
; display_handle_event Обработка события (из IRQ)
proc display_handle_event
test eax, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
jz .done
cmp dword [vbox_display_enabled], 0
je .done
; Запомнить время события (сбросить таймер при повторных)
invoke GetTimerTicks
mov [vbox_display_event_time], eax
; Поставить флаг
mov dword [vbox_display_pending], 1
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Event queued, will apply in 2s\n"
.done:
ret
endp
; display_tick Вызывается периодически из timer tick
proc display_tick
cmp dword [vbox_display_pending], 0
je .done
invoke GetTimerTicks
sub eax, [vbox_display_event_time]
cmp eax, DISPLAY_DELAY_TICKS
jb .done
; Сбросить флаг ДО обработки (новое событие во время display_change
; не потеряется оно поставит pending заново)
mov dword [vbox_display_pending], 0
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Delay elapsed, applying...\n"
call display_change
.done:
ret
endp
; display_change Запросить и применить новое разрешение
proc display_change uses ebx ecx edx esi edi
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] display_change\n"
mov ebx, [vbox_device.display_virt]
test ebx, ebx
jz .skip
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.header.rc], 0
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.event_ack], 1
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.display], 0
mov eax, [vbox_device.display_phys]
stdcall vmmdev_send_request, eax
mov ebx, [vbox_device.display_virt]
mov edi, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.x_res]
mov esi, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.y_res]
mov ecx, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.bpp]
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Requested: %dx%d %dbpp\n", edi, esi, ecx
; Валидация
cmp edi, DISP_W_MIN
jb .skip
cmp esi, DISP_H_MIN
jb .skip
cmp edi, DISP_W_MAX
ja .skip
cmp esi, DISP_H_MAX
ja .skip
cmp ecx, 32
je .valid
cmp ecx, 24
je .valid
cmp ecx, 16
je .valid
cmp ecx, 8
jne .skip
.valid:
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Setting %dx%d %dbpp\n", edi, esi, ecx
; BGA под cli (как в v5)
cli
bga_set_mode edi, esi, ecx
sti
; Обновить DISPLAY структуру ядра
mov eax, [kos_display_ptr]
mov [eax + DISPLAY.width], edi
mov [eax + DISPLAY.height], esi
mov [eax + DISPLAY.bits_per_pixel], ecx
; pitch = (bpp / 8) * width
push ebx
mov ebx, ecx
shr ecx, 3
imul ecx, edi
mov [eax + DISPLAY.pitch], ecx
pop ebx
; SetScreen(width-1, height-1)
mov eax, edi
mov edx, esi
dec eax
dec edx
invoke SetScreen
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Resolution set successfully\n"
.skip:
ret
endp
; display_disable Отключить подсистему дисплея
proc display_disable
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Disabling...\n"
mov dword [vbox_display_enabled], 0
mov dword [vbox_display_pending], 0
xor eax, eax
ret
endp
REGISTER_SERVICE svc_display_name, DISPLAY_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_GRAPHICS, \
vmmdev_display_init, 0, display_disable, display_handle_event, display_tick, AUTOSTART_DISPLAY

View File

@@ -0,0 +1,263 @@
; =============================================================================
; Модуль : VBox Guest Properties Service
; Назначение : Обмен ключ-значение свойствами между хостом и гостем
; Файл : services/guest_props/guest_props.inc
; Протокол : HGCM (VBoxGuestPropSvc)
;
; Функционал:
; - Подключение к HGCM сервису VBoxGuestPropSvc
; - Установка начальных свойств гостевой ОС
; - GET_PROP / SET_PROP_VALUE / DEL_PROP / ENUM_PROPS
;
; Стандартные свойства (устанавливаются при инициализации):
; /VirtualBox/GuestInfo/OS/Product = "KolibriOS"
; /VirtualBox/GuestInfo/OS/Release = "1.0"
; /VirtualBox/GuestAdd/VersionExt = "1.0.0"
; /VirtualBox/GuestAdd/Revision = "1"
;
; Аналог в Linux: VBoxService --property
; =============================================================================
svc_guestprop_name db "GUEST_PROPS", 0
; HGCM service name on host
align 4
sz_guestprop_hgcm_service db 'VBoxGuestPropSvc', 0
; --- Guest Properties Data ---
align 4
gp_state GUEST_PROP_STATE
; Буферы выделяются динамически в guestprop_init, указатели в gp_state
; --- Строки стандартных свойств ---
align 4
gp_key_os_product db '/VirtualBox/GuestInfo/OS/Product', 0
gp_val_os_product db 'KolibriOS', 0
gp_key_os_release db '/VirtualBox/GuestInfo/OS/Release', 0
gp_val_os_release db '1.0', 0
gp_key_version db '/VirtualBox/GuestAdd/VersionExt', 0
gp_val_version db '1.0.0', 0
gp_key_revision db '/VirtualBox/GuestAdd/Revision', 0
gp_val_revision db '1', 0
; guestprop_init Инициализация сервиса Guest Properties
proc guestprop_init uses ebx ecx edx esi edi
DEBUGF 2, "[VBoxGuest] [GuestProp] Initializing...\n"
; Проверка что HGCM готов
mov eax, [vbox_device.hgcm_connect_virt]
test eax, eax
jz .hgcm_not_ready
; Уже подключены?
cmp dword [gp_state.connected], 1
je .already_connected
; --- Выделить буферы ---
invoke KernelAlloc, 4096 ; gp_enum_buf (page-aligned для HGCM)
test eax, eax
jz .alloc_fail
mov [gp_state.enum_buf_ptr], eax
invoke KernelAlloc, 4096 ; small_bufs: parms+name+value+flags
test eax, eax
jz .alloc_fail_free1
mov [gp_state.small_bufs_ptr], eax
; Вычислить указатели внутри small_bufs
lea ecx, [eax + 0]
mov [gp_state.parms_ptr], ecx ; +0: 48 байт (sizeof.HGCM_PARM * 4)
lea ecx, [eax + 48]
mov [gp_state.name_buf_ptr], ecx ; +48: 256 байт
lea ecx, [eax + 304]
mov [gp_state.value_buf_ptr], ecx ; +304: 1024 байт
lea ecx, [eax + 1328]
mov [gp_state.flags_buf_ptr], ecx ; +1328: 128 байт
DEBUGF 2, "[VBoxGuest] [GuestProp] Buffers allocated\n"
; Подключиться к HGCM сервису
DEBUGF 2, "[VBoxGuest] [GuestProp] Connecting to '%s'...\n", sz_guestprop_hgcm_service
stdcall hgcm_connect, sz_guestprop_hgcm_service
test eax, eax
jz .connect_failed
mov [gp_state.client_id], eax
mov dword [gp_state.connected], 1
DEBUGF 2, "[VBoxGuest] [GuestProp] Connected, client_id=%d\n", eax
; Установить начальные свойства
call guestprop_set_initial_props
DEBUGF 2, "[VBoxGuest] [GuestProp] Initialized OK\n"
xor eax, eax
ret
.connect_failed:
DEBUGF 2, "[VBoxGuest] [GuestProp] HGCM connect failed\n"
call guestprop_free_bufs
mov eax, VERR_HGCM_SERVICE_NOT_FOUND
ret
.alloc_fail_free1:
invoke KernelFree, [gp_state.enum_buf_ptr]
mov dword [gp_state.enum_buf_ptr], 0
.alloc_fail:
DEBUGF 2, "[VBoxGuest] [GuestProp] KernelAlloc failed\n"
mov eax, VERR_NO_MEMORY
ret
.hgcm_not_ready:
DEBUGF 2, "[VBoxGuest] [GuestProp] HGCM not ready\n"
mov eax, VERR_NOT_READY
ret
.already_connected:
DEBUGF 2, "[VBoxGuest] [GuestProp] Already connected\n"
xor eax, eax
ret
endp
; guestprop_set_initial_props Установить стандартные свойства ОС
proc guestprop_set_initial_props uses eax
DEBUGF 2, "[VBoxGuest] [GuestProp] Setting initial properties...\n"
stdcall guestprop_set, gp_key_os_product, gp_val_os_product
stdcall guestprop_set, gp_key_os_release, gp_val_os_release
stdcall guestprop_set, gp_key_version, gp_val_version
stdcall guestprop_set, gp_key_revision, gp_val_revision
DEBUGF 2, "[VBoxGuest] [GuestProp] Initial properties set\n"
ret
endp
; guestprop_set Установить свойство (SET_PROP_VALUE, fn=4)
proc guestprop_set stdcall uses ebx ecx edx esi edi, name_ptr:dword, value_ptr:dword
cmp dword [gp_state.connected], 0
je .not_connected
DEBUGF 2, "[VBoxGuest] [GuestProp] SET '%s' = '%s'\n", [name_ptr], [value_ptr]
mov edi, [gp_state.parms_ptr]
; parm[0] = name (linaddr IN)
stdcall hgcm_strlen, [name_ptr]
inc eax ; +null
stdcall hgcm_param_linaddr_in, edi, [name_ptr], eax
add edi, sizeof.HGCM_PARM
; parm[1] = value (linaddr IN)
stdcall hgcm_strlen, [value_ptr]
inc eax ; +null
stdcall hgcm_param_linaddr_in, edi, [value_ptr], eax
; Вызвать HGCM
stdcall hgcm_call32_pagelist, [gp_state.client_id], GUEST_PROP_FN_SET_PROP_VALUE, [gp_state.parms_ptr], GUEST_PROP_SET_VALUE_PARM_COUNT
test eax, eax
jnz .failed
DEBUGF 2, "[VBoxGuest] [GuestProp] SET OK\n"
xor eax, eax
ret
.not_connected:
mov eax, VERR_NOT_READY
ret
.failed:
DEBUGF 2, "[VBoxGuest] [GuestProp] SET failed: 0x%x\n", eax
ret
; strlen(esi) eax
endp
; guestprop_set_with_flags Установить свойство с флагами (SET_PROP, fn=2)
proc guestprop_set_with_flags stdcall uses ebx ecx edx esi edi, name_ptr:dword, value_ptr:dword, flags_ptr:dword
cmp dword [gp_state.connected], 0
je .not_connected
mov edi, [gp_state.parms_ptr]
; parm[0] = name
stdcall hgcm_strlen, [name_ptr]
inc eax
stdcall hgcm_param_linaddr_in, edi, [name_ptr], eax
add edi, sizeof.HGCM_PARM
; parm[1] = value
stdcall hgcm_strlen, [value_ptr]
inc eax
stdcall hgcm_param_linaddr_in, edi, [value_ptr], eax
add edi, sizeof.HGCM_PARM
; parm[2] = flags
stdcall hgcm_strlen, [flags_ptr]
inc eax
stdcall hgcm_param_linaddr_in, edi, [flags_ptr], eax
stdcall hgcm_call32_pagelist, [gp_state.client_id], GUEST_PROP_FN_SET_PROP, [gp_state.parms_ptr], GUEST_PROP_SET_PARM_COUNT
ret
.not_connected:
mov eax, VERR_NOT_READY
ret
endp
proc hgcm_strlen uses esi, str_ptr:dword
mov esi, [str_ptr]
xor eax, eax
@@: cmp byte [esi], 0
je @f
inc esi
inc eax
jmp @b
@@: ret
endp
; guestprop_disable Отключение сервиса
proc guestprop_disable uses edi
DEBUGF 2, "[VBoxGuest] [GuestProp] Disabling...\n"
cmp dword [gp_state.connected], 0
je .done
stdcall hgcm_disconnect, [gp_state.client_id]
mov dword [gp_state.connected], 0
mov dword [gp_state.client_id], 0
; Освободить буферы
call guestprop_free_bufs
DEBUGF 2, "[VBoxGuest] [GuestProp] Disabled\n"
.done:
xor eax, eax
ret
endp
; guestprop_free_bufs Освободить буферы Guest Properties
proc guestprop_free_bufs
cmp dword [gp_state.enum_buf_ptr], 0
je @f
invoke KernelFree, [gp_state.enum_buf_ptr]
mov dword [gp_state.enum_buf_ptr], 0
@@:
cmp dword [gp_state.small_bufs_ptr], 0
je @f
invoke KernelFree, [gp_state.small_bufs_ptr]
mov dword [gp_state.small_bufs_ptr], 0
; Обнулить указатели-потомки
mov dword [gp_state.parms_ptr], 0
mov dword [gp_state.name_buf_ptr], 0
mov dword [gp_state.value_buf_ptr], 0
mov dword [gp_state.flags_buf_ptr], 0
@@:
ret
endp
REGISTER_SERVICE svc_guestprop_name, GUEST_PROP_EVENT_MASK, GUEST_PROP_CAPS_MASK, \
guestprop_init, 0, guestprop_disable, 0, 0, AUTOSTART_GUEST_PROPS

View File

@@ -0,0 +1,243 @@
; =============================================================================
; Модуль : VMMDev Heartbeat Service
; Назначение : Периодическая отправка heartbeat сигналов хосту для мониторинга
; Файл : vmmdev/heartbeat.inc
; Протокол : Прямые VMMDev запросы (БЕЗ HGCM)
; =============================================================================
svc_heartbeat_name db "HEARTBEAT", 0
; --- Heartbeat Data ---
align 4
vbox_hb_last_send_time dd 0 ; timer_ticks последней отправки
vbox_hb_tick_interval dd 100
vbox_hb_enabled dd 0
; Статические буферы для запросов
align 4
vmmdev_heartbeat_configure_s VMMDEV_HEARTBEAT_CONFIGURE \
<sizeof.VMMDEV_HEARTBEAT_CONFIGURE, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_HEARTBEAT_CONFIGURE, \
0, 0, 0>, \
0, 0, 0; интервал и enabled заполняются при вызове
align 4
vmmdev_heartbeat_s VMMDEV_GUEST_HEARTBEAT \
<sizeof.VMMDEV_GUEST_HEARTBEAT, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_GUEST_HEARTBEAT, \
0, 0, 0>
; vmmdev_heartbeat_init Инициализация heartbeat подсистемы
proc vmmdev_heartbeat_init
DEBUGF 2, "[VBoxGuest] [Heartbeat] Initializing...\n"
; Получить физические адреса буферов
mov eax, vmmdev_heartbeat_configure_s
mov [vbox_device.heartbeat_config_virt], eax
invoke GetPhysAddr
mov [vbox_device.heartbeat_config_phys], eax
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Config packet: virt=0x%x, phys=0x%x\n", \
vmmdev_heartbeat_configure_s, eax
mov eax, vmmdev_heartbeat_s
mov [vbox_device.heartbeat_virt], eax
invoke GetPhysAddr
mov [vbox_device.heartbeat_phys], eax
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Send packet : virt=0x%x, phys=0x%x\n", \
vmmdev_heartbeat_s, eax
; Запросить конфигурацию у хоста
call vmmdev_heartbeat_configure
test eax, eax
jnz .failed
DEBUGF 2, "[VBoxGuest] [Heartbeat] Initialized successfully\n"
xor eax, eax
ret
.failed:
DEBUGF 2, "[VBoxGuest] [Heartbeat] Initialization failed: rc=0x%x\n", eax
ret
endp
; vmmdev_heartbeat_configure Запросить настройки heartbeat у хоста и включить мониторинг
proc vmmdev_heartbeat_configure uses ebx edi
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Configuring...\n"
mov edi, [vbox_device.heartbeat_config_virt]
test edi, edi
jz .bad_ptr
; Заполнить запрос (header уже инициализирован статически)
; Обнулить rc перед отправкой
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc], 0
; Запросить у хоста интервал (0 = использовать дефолтный)
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval], 0
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval_high], 0
; Включить heartbeat
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.f_enabled], 1
; Отправить запрос
mov ebx, [vbox_device.heartbeat_config_phys]
stdcall vmmdev_send_request, ebx
; Проверить результат
mov eax, [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc]
test eax, eax
js .error
; Получить интервал от хоста (если вернул)
mov eax, [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval]
mov edx, [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval_high]
; Конвертировать наносекунды в тики
; Предположим timer tick = 10ms = 10,000,000 ns
; Делим ns на 10,000,000 чтобы получить тики
test eax, eax
jz .use_default
test edx, edx
jnz .use_default ; Если интервал > 4.2 секунды, используем дефолт
; eax = наносекунды, конвертируем в тики (10ms)
; ticks = ns / 10,000,000
; правильное деление: 10,000,000 / 10,000 = 1000 (тики в 10ms)
mov ebx, 10000
xor edx, edx
div ebx ; eax = eax / 10000 (микросекунды)
mov ebx, 1000
xor edx, edx
div ebx ; eax = eax / 1000 (тики 10ms)
test eax, eax
jz .use_default
mov [vbox_hb_tick_interval], eax
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Interval set to %d ticks\n", eax
jmp .enabled
.use_default:
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Using default interval: %d ticks\n", \
[vbox_hb_tick_interval]
.enabled:
; Включить heartbeat
mov dword [vbox_hb_enabled], 1
DEBUGF 2, "[VBoxGuest] [Heartbeat] Enabled\n"
xor eax, eax
ret
.error:
DEBUGF 2, "[VBoxGuest] [Heartbeat] Configure failed: rc=0x%x\n", eax
ret
.bad_ptr:
DEBUGF 2, "[VBoxGuest] [Heartbeat] Configure: bad pointer\n"
mov eax, VERR_INVALID_POINTER
ret
endp
; vmmdev_heartbeat_send Отправить heartbeat хосту
proc vmmdev_heartbeat_send uses ebx edi
mov edi, [vbox_device.heartbeat_virt]
mov ebx, [vbox_device.heartbeat_phys]
test edi, edi
jz .bad_ptr
test ebx, ebx
jz .bad_ptr
; Обнулить rc перед отправкой
mov dword [edi + VMMDEV_GUEST_HEARTBEAT.header.rc], 0
; DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Sending...\n"
; Отправить запрос
stdcall vmmdev_send_request, ebx
; Проверить результат
mov eax, [edi + VMMDEV_GUEST_HEARTBEAT.header.rc]
test eax, eax
js .error
; DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Sent successfully\n"
xor eax, eax
ret
.error:
DEBUGF 2, "[VBoxGuest] [Heartbeat] Send failed: rc=0x%x\n", eax
ret
.bad_ptr:
DEBUGF 2, "[VBoxGuest] [Heartbeat] Send: bad pointer virt=0x%x phys=0x%x\n", edi, ebx
mov eax, VERR_INVALID_POINTER
ret
endp
proc vmmdev_heartbeat_tick
cmp dword [vbox_hb_enabled], 0
je .done
invoke GetTimerTicks ; eax = timer_ticks
sub eax, [vbox_hb_last_send_time]
cmp eax, [vbox_hb_tick_interval]
jb .done
invoke GetTimerTicks ; eax = текущее время для обновления last_send_time
mov [vbox_hb_last_send_time], eax
call vmmdev_heartbeat_send
test eax, eax
jz .done
DEBUGF 2, "[VBoxGuest] [Heartbeat] Tick: send error 0x%x\n", eax
.done:
ret
endp
; vmmdev_heartbeat_disable Отключить heartbeat (при выгрузке драйвера)
proc vmmdev_heartbeat_disable uses ebx edi
DEBUGF 2, "[VBoxGuest] [Heartbeat] Disabling...\n"
; Выключить локальный флаг
mov dword [vbox_hb_enabled], 0
mov edi, [vbox_device.heartbeat_config_virt]
test edi, edi
jz .bad_ptr
; Обнулить rc
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc], 0
; Отключить на хосте
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.f_enabled], 0
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval], 0
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval_high], 0
; Отправить запрос
mov ebx, [vbox_device.heartbeat_config_phys]
stdcall vmmdev_send_request, ebx
mov eax, [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc]
test eax, eax
js .error
DEBUGF 2, "[VBoxGuest] [Heartbeat] Disabled\n"
xor eax, eax
ret
.error:
DEBUGF 2, "[VBoxGuest] [Heartbeat] Disable failed: rc=0x%x\n", eax
ret
.bad_ptr:
mov eax, VERR_INVALID_POINTER
ret
endp
REGISTER_SERVICE svc_heartbeat_name, 0, 0, \
vmmdev_heartbeat_init, 0, vmmdev_heartbeat_disable, 0, vmmdev_heartbeat_tick, AUTOSTART_HEARTBEAT

View File

@@ -0,0 +1,382 @@
; =============================================================================
; Модуль : VMMDev Mouse Service (Absolute Pointer)
; Назначение : Поддержка абсолютных координат мыши VirtualBox
; Файл : services/mouse/mouse.inc
;
; Использует расширенный протокол GetMouseStatusEx (type=223) если хост
; поддерживает FULL_STATE_PROTOCOL кнопки и скролл приходят от VMMDev.
; Если хост не поддерживает fallback на стандартный протокол + PS/2.
; =============================================================================
svc_mouse_name db "MOUSE", 0
; --- Mouse Data ---
align 4
vbox_mouse_enabled dd 0
vbox_mouse_x dd 0
vbox_mouse_y dd 0
vbox_mouse_buttons dd 0
vbox_mouse_dz dd 0 ; vertical scroll delta
vbox_mouse_dw dd 0 ; horizontal scroll delta
vbox_mouse_host_features dd 0
vbox_mouse_use_ext dd 0 ; 1 = extended protocol (buttons+scroll from VMMDev)
; Статический буфер для запроса (48 байт: 36 standard + 12 extended fields)
align 4
vmmdev_mouse_status_s VMMDEV_REQ_MOUSE_STATUS \
<sizeof.VMMDEV_REQ_MOUSE_STATUS, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_SET_MOUSE_STATUS, \
0, 0, VMMDEV_REQUESTOR_KERNEL>, \
VBOX_MOUSE_GUEST_FEATURES, 0, 0
; Extended fields (immediately after VMMDEV_REQ_MOUSE_STATUS in memory)
; VMMDev заполнит их при GET_MOUSE_STATUS_EX (type=223)
vmmdev_mouse_ext_dz dd 0 ; offset +36: vertical scroll
vmmdev_mouse_ext_dw dd 0 ; offset +40: horizontal scroll
vmmdev_mouse_ext_buttons dd 0 ; offset +44: button state
SIZEOF_MOUSE_STATUS_EX = sizeof.VMMDEV_REQ_MOUSE_STATUS + 12
MOUSE_EXT_OFS_DZ = sizeof.VMMDEV_REQ_MOUSE_STATUS
MOUSE_EXT_OFS_DW = sizeof.VMMDEV_REQ_MOUSE_STATUS + 4
MOUSE_EXT_OFS_BUTTONS = sizeof.VMMDEV_REQ_MOUSE_STATUS + 8
; vmmdev_mouse_init Инициализация подсистемы мыши
proc vmmdev_mouse_init
; Guard against double initialization (fn_init = fn_enable in REGISTER_SERVICE)
cmp dword [vbox_mouse_enabled], 1
je .already_initialized
DEBUGF 2, "[VBoxGuest] [Mouse] Initializing...\n"
; Получить физический адрес буфера
mov eax, vmmdev_mouse_status_s
mov [vbox_device.mouse_virt], eax
invoke GetPhysAddr
mov [vbox_device.mouse_phys], eax
DEBUGF 2, "[VBoxGuest] [Mouse] Buffer: virt=0x%x, phys=0x%x, ext_size=%d\n", \
vmmdev_mouse_status_s, eax, SIZEOF_MOUSE_STATUS_EX
; Запросить текущий статус (стандартный GET, т.к. use_ext=0)
call mouse_query_status
test eax, eax
jnz .failed
DEBUGF 2, "[VBoxGuest] [Mouse] Host features: 0x%x\n", [vbox_mouse_host_features]
DEBUGF 2, "[VBoxGuest] [Mouse] Host position: X=%d, Y=%d\n", [vbox_mouse_x], [vbox_mouse_y]
; Проверить поддержку расширенного протокола
test dword [vbox_mouse_host_features], VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL
jz .no_ext
; === Расширенный протокол ===
mov dword [vbox_mouse_use_ext], 1
DEBUGF 2, "[VBoxGuest] [Mouse] Host supports FULL_STATE_PROTOCOL — using extended mode\n"
call mouse_send_status
test eax, eax
jnz .failed
; Отключить PS/2 IRQ12 VMMDev сам сообщает кнопки+скролл.
; Если PS/2 остаётся активным, SetMouseData(buttons=0) от VMMDev
; сбрасывает нажатые кнопки из PS/2 на каждом движении (glitch при drag).
stdcall i8042_mouse_control, 0
DEBUGF 2, "[VBoxGuest] [Mouse] PS/2 IRQ12 disabled (buttons from VMMDev)\n"
jmp .ok
.no_ext:
; === Стандартный протокол (fallback) ===
mov dword [vbox_mouse_use_ext], 0
DEBUGF 2, "[VBoxGuest] [Mouse] WARNING: Host does NOT support FULL_STATE_PROTOCOL\n"
DEBUGF 2, "[VBoxGuest] [Mouse] Fallback: standard protocol, PS/2 stays enabled for buttons\n"
call mouse_send_status
test eax, eax
jnz .failed
.ok:
DEBUGF 2, "[VBoxGuest] [Mouse] Initialized OK (ext=%d)\n", [vbox_mouse_use_ext]
xor eax, eax
ret
.already_initialized:
DEBUGF 2, "[VBoxGuest] [Mouse] Already initialized (skip reinit)\n"
xor eax, eax
ret
.failed:
DEBUGF 2, "[VBoxGuest] [Mouse] Init failed: 0x%x\n", eax
ret
endp
; mouse_query_status Запросить статус у хоста
proc mouse_query_status
push ebx edi
mov edi, [vbox_device.mouse_virt]
test edi, edi
jz .bad_ptr
; Очистить rc
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
; Выбрать тип запроса
cmp dword [vbox_mouse_use_ext], 0
je .setup_standard
; === Extended GET (type=223, size=48) ===
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], SIZEOF_MOUSE_STATUS_EX
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_GET_MOUSE_STATUS_EX
jmp .send
.setup_standard:
; === Standard GET (type=1, size=36) ===
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_GET_MOUSE_STATUS
.send:
mov ebx, [vbox_device.mouse_phys]
stdcall vmmdev_send_request, ebx
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc]
test eax, eax
js .error
; === Сохранить общие данные ===
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features]
mov [vbox_mouse_host_features], eax
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_x_pos]
mov [vbox_mouse_x], eax
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_y_pos]
mov [vbox_mouse_y], eax
; === Если extended сохранить кнопки и скролл ===
cmp dword [vbox_mouse_use_ext], 0
je .no_ext
mov eax, [edi + MOUSE_EXT_OFS_BUTTONS]
and eax, 0x1F ; биты 0-4: left, right, middle, X1, X2
mov [vbox_mouse_buttons], eax
mov eax, [edi + MOUSE_EXT_OFS_DZ]
mov [vbox_mouse_dz], eax
mov eax, [edi + MOUSE_EXT_OFS_DW]
mov [vbox_mouse_dw], eax
.no_ext:
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] VMMDev: feat=0x%x, X=%d, Y=%d, btn=0x%x, dz=%d\n", \
[vbox_mouse_host_features], [vbox_mouse_x], [vbox_mouse_y], \
[vbox_mouse_buttons], [vbox_mouse_dz]
; Восстановить header для SET запросов
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_SET_MOUSE_STATUS
xor eax, eax
pop edi ebx
ret
.error:
DEBUGF 2, "[VBoxGuest] [Mouse] Query failed: rc=0x%x\n", eax
; Восстановить header даже при ошибке
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_SET_MOUSE_STATUS
pop edi ebx
ret
.bad_ptr:
mov eax, VERR_INVALID_POINTER
pop edi ebx
ret
endp
; mouse_send_status Отправить guest capabilities хосту
proc mouse_send_status
push ebx edi
mov edi, [vbox_device.mouse_virt]
mov ebx, [vbox_device.mouse_phys]
test edi, edi
jz .bad_ptr
test ebx, ebx
jz .bad_ptr
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_x_pos], 0
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_y_pos], 0
; Выбрать guest features
cmp dword [vbox_mouse_use_ext], 0
je .std_feats
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], VBOX_MOUSE_GUEST_FEATURES_EXT
DEBUGF 2, "[VBoxGuest] [Mouse] Sending guest features: 0x%x (extended)\n", VBOX_MOUSE_GUEST_FEATURES_EXT
jmp .send
.std_feats:
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], VBOX_MOUSE_GUEST_FEATURES
DEBUGF 2, "[VBoxGuest] [Mouse] Sending guest features: 0x%x (standard)\n", VBOX_MOUSE_GUEST_FEATURES
.send:
stdcall vmmdev_send_request, ebx
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc]
test eax, eax
js .error
mov dword [vbox_mouse_enabled], 1
xor eax, eax
pop edi ebx
ret
.error:
DEBUGF 2, "[VBoxGuest] [Mouse] Send status failed: 0x%x\n", eax
pop edi ebx
ret
.bad_ptr:
mov eax, VERR_INVALID_POINTER
pop edi ebx
ret
endp
; mouse_handle_event Обработка событий мыши (вызывается из IRQ)
proc mouse_handle_event
push eax
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] Event: mask=0x%x\n", eax
; Любое из двух событий нужен свежий статус
call mouse_query_status
test eax, eax
jnz .query_fail
pop eax
push eax
; Для position changed обновить координаты в системе
test eax, VMMDEV_EVENT_MOUSE_POSITION_CHANGED
jz .done
call mouse_update_system_position
jmp .done
.query_fail:
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] Query failed in event handler: 0x%x\n", eax
.done:
pop eax
ret
endp
; mouse_update_system_position Отправить позицию + кнопки + скролл в KolibriOS
proc mouse_update_system_position
push eax ebx ecx edx
; VirtualBox: 0-65535, KolibriOS: 0-32767
mov ecx, [vbox_mouse_x]
shr ecx, 1 ; X / 2
mov edx, [vbox_mouse_y]
shr edx, 1 ; Y / 2
; Флаги: bit31=X_abs, bit30=Y_abs + кнопки (bits 0-4)
mov eax, 0xC0000000
or eax, [vbox_mouse_buttons]
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] SetMouseData: flags=0x%x, X=%d, Y=%d, btn=0x%x, dz=%d, dw=%d\n", \
eax, ecx, edx, [vbox_mouse_buttons], [vbox_mouse_dz], [vbox_mouse_dw]
invoke SetMouseData, eax, ecx, edx, [vbox_mouse_dz], [vbox_mouse_dw]
; Сбросить дельты скролла (это дельты, не состояние)
mov dword [vbox_mouse_dz], 0
mov dword [vbox_mouse_dw], 0
pop edx ecx ebx eax
ret
endp
; mouse_disable Отключить мышь
proc mouse_disable
push ebx edi
DEBUGF 2, "[VBoxGuest] [Mouse] Disabling...\n"
mov dword [vbox_mouse_enabled], 0
mov edi, [vbox_device.mouse_virt]
test edi, edi
jz .done
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], 0
mov ebx, [vbox_device.mouse_phys]
stdcall vmmdev_send_request, ebx
; Восстановить PS/2 при выключении
stdcall i8042_mouse_control, 1
.done:
xor eax, eax
pop edi ebx
ret
endp
; i8042_mouse_control Включить/выключить IRQ12 PS/2 мыши
proc i8042_mouse_control stdcall, enable:dword
call .wait_input
mov al, 0x20
out 0x64, al
call .wait_output
in al, 0x60
and al, not 0x02
cmp [enable], 0
je @f
or al, 0x02
@@:
mov ah, al
call .wait_input
mov al, 0x60
out 0x64, al
call .wait_input
mov al, ah
out 0x60, al
ret
.wait_input:
push ecx
mov ecx, 0xFFFF
@@:
in al, 0x64
test al, 0x02
jz @f
dec ecx
jnz @b
@@:
pop ecx
ret
.wait_output:
push ecx
mov ecx, 0xFFFF
@@:
in al, 0x64
test al, 0x01
jnz @f
dec ecx
jnz @b
@@:
pop ecx
ret
endp
REGISTER_SERVICE svc_mouse_name, MOUSE_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_MOUSE, \
vmmdev_mouse_init, vmmdev_mouse_init, mouse_disable, mouse_handle_event, 0, AUTOSTART_MOUSE

View File

@@ -0,0 +1,265 @@
; =============================================================================
; Модуль : VMMDev Seamless Mode Service
; Назначение : Получение запросов на seamless mode от хоста
; Файл : services/seamless/seamless.inc
; Протокол : VMMDevReq_GetSeamlessChangeRequest (73)
;
; Seamless mode = «бесшовные окна»: окна гостя рисуются как окна хоста,
; без рамки VM. Хост периодически спрашивает гостя: «какие области экрана
; заняты окнами?» (visible region).
;
; Для KolibriOS полный seamless пока невозможен (нет API получения
; регионов окон), но мы:
; 1. Сообщаем хосту VMMDEV_GUEST_SUPPORTS_SEAMLESS кнопка
; «View Seamless Mode» в VBox GUI станет активной
; 2. Получаем события SEAMLESS_MODE_CHANGE через IRQ
; 3. Запрашиваем текущий режим через GetSeamlessChangeRequest
; 4. При включении seamless отправляем visible region = весь экран
; (один прямоугольник 0,0 width,height)
;
; Архитектура (как display):
; IRQ event seamless_handle_event pending=1, timestamp
; Timer tick seamless_tick если pending и debounce прошёл обработать
; =============================================================================
svc_seamless_name db "SEAMLESS", 0
; --- Data ---
align 4
vbox_seamless_enabled dd 0
vbox_seamless_current_mode dd 0 ; VMMDEV_SEAMLESS_*
vbox_seamless_pending dd 0
vbox_seamless_event_time dd 0
; Debounce: 500ms (50 тиков * 10ms)
SEAMLESS_DELAY_TICKS equ 50
; --- Пакет GetSeamlessChangeRequest ---
align 4
vmmdev_seamless_change_s VMMDEV_SEAMLESS_CHANGE_REQUEST \
<sizeof.VMMDEV_SEAMLESS_CHANGE_REQUEST, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_GET_SEAMLESS_CHANGE, \
0, 0, 0>, \
0, \
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE ; eventAck
align 4
seamless_change_virt dd 0
seamless_change_phys dd 0
; --- Пакет VideoSetVisibleRegion (1 прямоугольник) ---
align 4
vmmdev_visible_region_s VMMDEV_VIDEO_SET_VISIBLE_REGION \
<sizeof.VMMDEV_VIDEO_SET_VISIBLE_REGION, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_VIDEO_SET_VISIBLE_REGION, \
0, 0, 0>, \
1, \
<0, 0, 0, 0> ; rect0: заполняется при вызове
align 4
visible_region_virt dd 0
visible_region_phys dd 0
; seamless_init Инициализация
proc seamless_init uses ebx
DEBUGF 2, "[VBoxGuest] [Seamless] Initializing...\n"
; --- Физические адреса пакетов ---
mov eax, vmmdev_seamless_change_s
mov [seamless_change_virt], eax
invoke GetPhysAddr
test eax, eax
jz .phys_failed
mov [seamless_change_phys], eax
mov eax, vmmdev_visible_region_s
mov [visible_region_virt], eax
invoke GetPhysAddr
test eax, eax
jz .phys_failed
mov [visible_region_phys], eax
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] SeamlessChange pkt: virt=0x%x phys=0x%x\n", \
[seamless_change_virt], [seamless_change_phys]
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] VisibleRegion pkt: virt=0x%x phys=0x%x\n", \
[visible_region_virt], [visible_region_phys]
mov dword [vbox_seamless_enabled], 1
mov dword [vbox_seamless_current_mode], VMMDEV_SEAMLESS_DISABLED
; Сразу запросить текущий режим (может быть уже включен)
call seamless_query_mode
DEBUGF 2, "[VBoxGuest] [Seamless] Initialized OK (mode=%d)\n", \
[vbox_seamless_current_mode]
xor eax, eax
ret
.phys_failed:
DEBUGF 2, "[VBoxGuest] [Seamless] GetPhysAddr failed\n"
mov eax, VERR_GENERAL_FAILURE
ret
endp
; seamless_handle_event Из IRQ контекста
proc seamless_handle_event
test eax, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE
jz .done
cmp dword [vbox_seamless_enabled], 0
je .done
invoke GetTimerTicks
mov [vbox_seamless_event_time], eax
mov dword [vbox_seamless_pending], 1
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] Event queued\n"
.done:
ret
endp
; seamless_tick Из таймера, debounce
proc seamless_tick
cmp dword [vbox_seamless_pending], 0
je .done
invoke GetTimerTicks
sub eax, [vbox_seamless_event_time]
cmp eax, SEAMLESS_DELAY_TICKS
jb .done
mov dword [vbox_seamless_pending], 0
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] Processing mode change...\n"
call seamless_query_mode
.done:
ret
endp
; seamless_query_mode Запросить текущий seamless mode у хоста (req 73)
proc seamless_query_mode uses ebx edi
mov edi, [seamless_change_virt]
test edi, edi
jz .bad_ptr
; Заполнить запрос
mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.header.rc], 0
mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.mode], 0
mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.eventAck], \
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE
; Отправить
stdcall vmmdev_send_request, [seamless_change_phys]
; Проверить rc
mov eax, [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.header.rc]
test eax, eax
jnz .error
; Прочитать режим
mov eax, [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.mode]
mov ebx, [vbox_seamless_current_mode]
mov [vbox_seamless_current_mode], eax
DEBUGF 2, "[VBoxGuest] [Seamless] Mode: %d → %d\n", ebx, eax
; Обработать смену режима
cmp eax, VMMDEV_SEAMLESS_DISABLED
je .mode_disabled
cmp eax, VMMDEV_SEAMLESS_VISIBLE_REGION
je .mode_visible_region
cmp eax, VMMDEV_SEAMLESS_HOST_WINDOW
je .mode_host_window
DEBUGF 2, "[VBoxGuest] [Seamless] Unknown mode: %d\n", eax
ret
.mode_disabled:
DEBUGF 2, "[VBoxGuest] [Seamless] Seamless DISABLED\n"
ret
.mode_visible_region:
DEBUGF 2, "[VBoxGuest] [Seamless] Seamless ENABLED (visible region)\n"
; Отправить видимую область = весь экран
call seamless_send_fullscreen_region
ret
.mode_host_window:
DEBUGF 2, "[VBoxGuest] [Seamless] Seamless ENABLED (host window)\n"
call seamless_send_fullscreen_region
ret
.bad_ptr:
DEBUGF 2, "[VBoxGuest] [Seamless] Bad pointer\n"
ret
.error:
DEBUGF 2, "[VBoxGuest] [Seamless] GetSeamlessChange failed: rc=0x%x\n", eax
ret
endp
; seamless_send_fullscreen_region Отправить visible region = весь экран
proc seamless_send_fullscreen_region uses ebx edi
mov edi, [visible_region_virt]
test edi, edi
jz .bad_ptr
; Получить текущее разрешение из KOS
invoke GetDisplay
test eax, eax
jz .no_display
mov ebx, eax
mov ecx, [ebx + DISPLAY.width]
mov edx, [ebx + DISPLAY.height]
DEBUGF 1, "[VBoxGuest] [Seamless] Sending region: 0,0 → %d,%d\n", ecx, edx
; Заполнить заголовок
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.header.rc], 0
; Заполнить прямоугольник
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.cRect], 1
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.xLeft], 0
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.yTop], 0
mov [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.xRight], ecx
mov [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.yBottom], edx
; Отправить
stdcall vmmdev_send_request, [visible_region_phys]
mov eax, [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.header.rc]
test eax, eax
jnz .error
DEBUGF 1, "[VBoxGuest] [Seamless] Region sent OK\n"
xor eax, eax
ret
.bad_ptr:
DEBUGF 2, "[VBoxGuest] [Seamless] Bad pointer\n"
ret
.no_display:
DEBUGF 2, "[VBoxGuest] [Seamless] No display\n"
ret
.error:
DEBUGF 2, "[VBoxGuest] [Seamless] SetVisibleRegion failed: rc=0x%x\n", eax
ret
endp
; seamless_disable Выключить seamless
proc seamless_disable
DEBUGF 2, "[VBoxGuest] [Seamless] Disabling...\n"
mov dword [vbox_seamless_enabled], 0
mov dword [vbox_seamless_pending], 0
mov dword [vbox_seamless_current_mode], VMMDEV_SEAMLESS_DISABLED
xor eax, eax
ret
endp
REGISTER_SERVICE svc_seamless_name, SEAMLESS_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_SEAMLESS, \
seamless_init, 0, seamless_disable, seamless_handle_event, seamless_tick, AUTOSTART_SEAMLESS

View File

@@ -0,0 +1,11 @@
; =============================================================================
; Services Aggregator
; =============================================================================
include 'services/mouse/mouse.inc'
include 'services/heartbeat/heartbeat.inc'
include 'services/guest_props/guest_props.inc'
include 'services/display/display.inc'
include 'services/shared_folders/shared_folders.inc'
include 'services/clipboard/clipboard.inc'
include 'services/seamless/seamless.inc'
include 'services/timesync/timesync.inc'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,444 @@
; =============================================================================
; Модуль : VMMDev Time Sync Service
; Назначение : Синхронизация часов гостевой ОС с хостом VirtualBox
; Файл : services/timesync/timesync.inc
;
; VBox возвращает 64-bit миллисекунды с Unix epoch (1970-01-01).
; Драйвер пишет время напрямую в CMOS RTC (порты 0x70/0x71).
; int 0x40 нельзя из kernel-mode: обработчик syscall предполагает ring 3
; (переключение стеков user→kernel), из ring 0 это разрушает стек.
; =============================================================================
svc_timesync_name db "TIMESYNC", 0
; --- Data ---
align 4
vbox_timesync_enabled dd 0
vbox_timesync_last_tick dd 0
align 4
vmmdev_get_host_time_s VMMDEV_GET_HOST_TIME \
<sizeof.VMMDEV_GET_HOST_TIME, \
VMMDEV_REQUEST_HEADER_VERSION, \
VMMDEV_REQ_GET_HOST_TIME, \
0, 0, 0>, \
0, 0
align 4
timesync_time_virt dd 0
timesync_time_phys dd 0
; CMOS port constants
CMOS_ADDR equ 0x70
CMOS_DATA equ 0x71
; timesync_init Инициализация
proc timesync_init
DEBUGF 2, "[VBoxGuest] [TimeSync] Initializing...\n"
mov eax, vmmdev_get_host_time_s
mov [timesync_time_virt], eax
invoke GetPhysAddr
mov [timesync_time_phys], eax
DEBUGF 2, "[VBoxGuest] [TimeSync] Buffer: virt=0x%x, phys=0x%x\n", \
[timesync_time_virt], [timesync_time_phys]
mov dword [vbox_timesync_enabled], 1
call timesync_do_sync
DEBUGF 2, "[VBoxGuest] [TimeSync] Initialized OK\n"
xor eax, eax
ret
endp
; timesync_tick Проверка интервала и синхронизация
proc timesync_tick
cmp dword [vbox_timesync_enabled], 0
je .done
invoke GetTimerTicks
sub eax, [vbox_timesync_last_tick]
cmp eax, TIMESYNC_INTERVAL_TICKS
jb .done
invoke GetTimerTicks
mov [vbox_timesync_last_tick], eax
call timesync_do_sync
.done:
ret
endp
; timesync_do_sync Получить время хоста и записать в CMOS RTC
proc timesync_do_sync uses ebx ecx edx esi edi
DEBUGF 2, "[VBoxGuest] [TimeSync] Syncing...\n"
mov edi, [timesync_time_virt]
test edi, edi
jz .bad_ptr
mov dword [edi + VMMDEV_GET_HOST_TIME.header.rc], 0
mov dword [edi + VMMDEV_GET_HOST_TIME.time_low], 0
mov dword [edi + VMMDEV_GET_HOST_TIME.time_high], 0
stdcall vmmdev_send_request, [timesync_time_phys]
mov eax, [edi + VMMDEV_GET_HOST_TIME.header.rc]
test eax, eax
js .error
; 64-bit миллисекунды с epoch
mov eax, [edi + VMMDEV_GET_HOST_TIME.time_low]
mov edx, [edi + VMMDEV_GET_HOST_TIME.time_high]
DEBUGF 2, "[VBoxGuest] [TimeSync] Host ms: hi=0x%x lo=0x%x\n", edx, eax
mov ecx, eax
or ecx, edx
jz .bad_time
; edx:eax / 1000 eax = unix seconds
mov ecx, 1000
div ecx
DEBUGF 2, "[VBoxGuest] [TimeSync] Unix timestamp: %d\n", eax
call timesync_set_cmos
DEBUGF 2, "[VBoxGuest] [TimeSync] Sync complete\n"
xor eax, eax
ret
.bad_ptr:
DEBUGF 2, "[VBoxGuest] [TimeSync] Bad pointer\n"
mov eax, VERR_INVALID_POINTER
ret
.error:
DEBUGF 2, "[VBoxGuest] [TimeSync] GetHostTime failed: rc=0x%x\n", eax
ret
.bad_time:
DEBUGF 2, "[VBoxGuest] [TimeSync] Zero time returned\n"
mov eax, VERR_GENERAL_FAILURE
ret
endp
; timesync_set_cmos Вычисляет дату/время и записывает в CMOS RTC
proc timesync_set_cmos uses ebx ecx edx esi edi
locals
ts_sec dd ?
ts_min dd ?
ts_hour dd ?
ts_day dd ?
ts_month dd ?
ts_year dd ?
ts_dow dd ?
endl
; ── Разложить на дни и время дня ──
xor edx, edx
mov ecx, 86400
div ecx
; eax = total_days, edx = time_of_day_seconds
mov esi, eax ; total_days
; Время суток
mov eax, edx
xor edx, edx
mov ecx, 3600
div ecx
mov [ts_hour], eax
mov eax, edx
xor edx, edx
mov ecx, 60
div ecx
mov [ts_min], eax
mov [ts_sec], edx
; ── День недели: (days + 3) % 7 + 1 ──
lea eax, [esi + 3]
xor edx, edx
mov ecx, 7
div ecx
inc edx
mov [ts_dow], edx
; ── civil_from_days (Howard Hinnant) ──
; eax = days дата
mov eax, esi
call .civil_from_days
mov [ts_day], eax
mov [ts_month], ebx
mov [ts_year], ecx
DEBUGF 2, "[VBoxGuest] [TimeSync] Date: %d-%d-%d %d:%d:%d DoW=%d\n", \
[ts_year], [ts_month], [ts_day], [ts_hour], [ts_min], [ts_sec], [ts_dow]
; ── Ждать окончания обновления RTC ──
call .cmos_wait
; ── Запретить обновления (SET bit в Status B) ──
mov al, 0x0B
out CMOS_ADDR, al
in al, CMOS_DATA
or al, 0x80
push eax
mov al, 0x0B
out CMOS_ADDR, al
pop eax
out CMOS_DATA, al
; ── Записать все поля (BCD) ──
mov eax, [ts_sec]
call .to_bcd
mov ah, al
mov al, 0x00 ; seconds register
call .cmos_wr
mov eax, [ts_min]
call .to_bcd
mov ah, al
mov al, 0x02 ; minutes register
call .cmos_wr
mov eax, [ts_hour]
call .to_bcd
mov ah, al
mov al, 0x04 ; hours register
call .cmos_wr
mov eax, [ts_dow]
mov ah, al
mov al, 0x06 ; weekday register
call .cmos_wr
mov eax, [ts_day]
call .to_bcd
mov ah, al
mov al, 0x07 ; day register
call .cmos_wr
mov eax, [ts_month]
call .to_bcd
mov ah, al
mov al, 0x08 ; month register
call .cmos_wr
; YY = year mod 100
mov eax, [ts_year]
xor edx, edx
mov ecx, 100
div ecx
push eax ; century
mov eax, edx
call .to_bcd
mov ah, al
mov al, 0x09 ; year register
call .cmos_wr
; Century
pop eax
call .to_bcd
mov ah, al
mov al, 0x32 ; century register
call .cmos_wr
; ── Разрешить обновления ──
mov al, 0x0B
out CMOS_ADDR, al
in al, CMOS_DATA
and al, 0x7F
push eax
mov al, 0x0B
out CMOS_ADDR, al
pop eax
out CMOS_DATA, al
ret
; ── Helpers ──
.cmos_wr:
; al = register, ah = value
out CMOS_ADDR, al
mov al, ah
out CMOS_DATA, al
ret
.cmos_wait:
push eax ecx
mov ecx, 10000
@@:
mov al, 0x0A
out CMOS_ADDR, al
in al, CMOS_DATA
test al, 0x80
jz @f
pause
loop @b
@@:
pop ecx eax
ret
.to_bcd:
; eax (0-99) eax BCD
push ecx edx
and eax, 0xFF
xor edx, edx
mov ecx, 10
div ecx
shl eax, 4
or eax, edx
pop edx ecx
ret
; .civil_from_days Howard Hinnant algorithm
.civil_from_days:
; z = days + 719468
add eax, 719468
; era = z / 146097
xor edx, edx
mov ecx, 146097
div ecx
mov esi, eax ; era
; doe = z - era * 146097 (но z уже потерян используем edx = remainder)
mov ecx, edx ; ecx = doe
; yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365
mov eax, ecx
xor edx, edx
mov edi, 1460
div edi
mov edi, eax ; doe/1460
mov eax, ecx
xor edx, edx
push ecx
mov ecx, 36524
div ecx
pop ecx
sub edi, eax ; doe/1460 - doe/36524 (note: we subtract then add later)
; Actually: val = doe - doe/1460 + doe/36524 - doe/146096
; Let me redo properly:
; A = doe/1460
push ecx
mov eax, ecx
xor edx, edx
mov ecx, 1460
div ecx
pop ecx
push eax ; [esp] = A
; B = doe/36524
push ecx
mov eax, ecx
xor edx, edx
mov ecx, 36524
div ecx
pop ecx
push eax ; [esp]=B, [esp+4]=A
; C = doe/146096
push ecx
mov eax, ecx
xor edx, edx
mov ecx, 146096
div ecx
pop ecx
; eax = C
pop ebx ; B
pop edi ; A
; val = doe - A + B - C
mov edx, ecx ; doe
sub edx, edi
add edx, ebx
sub edx, eax
; yoe = val / 365
mov eax, edx
xor edx, edx
push ecx
mov ecx, 365
div ecx
pop ecx
; eax = yoe, ecx still = doe
mov edi, eax ; edi = yoe
; year = yoe + era * 400
mov eax, esi ; era
imul eax, 400
add eax, edi
push eax ; [esp] = year
; doy = doe - (365*yoe + yoe/4 - yoe/100)
mov eax, edi
imul eax, 365
mov esi, eax ; 365*yoe
mov eax, edi
shr eax, 2
add esi, eax ; + yoe/4
mov eax, edi
xor edx, edx
push ecx
mov ecx, 100
div ecx
pop ecx
sub esi, eax ; - yoe/100
sub ecx, esi ; ecx = doy
; mp = (5*doy + 2) / 153
imul eax, ecx, 5
add eax, 2
xor edx, edx
push ecx
mov ecx, 153
div ecx
pop ecx
mov edi, eax ; mp
; day = doy - (153*mp + 2)/5 + 1
imul eax, edi, 153
add eax, 2
xor edx, edx
push ecx
mov ecx, 5
div ecx
pop ecx
sub ecx, eax
inc ecx ; ecx = day
; month = mp < 10 ? mp + 3 : mp - 9
cmp edi, 10
jae .mp_ge10
add edi, 3
jmp .month_done2
.mp_ge10:
sub edi, 9
.month_done2:
; edi = month, ecx = day
; if month <= 2 year++
pop eax ; year
cmp edi, 2
ja .year_ok2
inc eax
.year_ok2:
; Формируем результат: eax=day, ebx=month, ecx=year
mov ebx, edi ; month
xchg eax, ecx ; eax=day, ecx=year
ret
endp
; timesync_disable Выключить timesync
proc timesync_disable
DEBUGF 2, "[VBoxGuest] [TimeSync] Disabling...\n"
mov dword [vbox_timesync_enabled], 0
xor eax, eax
ret
endp
REGISTER_SERVICE svc_timesync_name, TIMESYNC_EVENT_MASK, 0, \
timesync_init, 0, timesync_disable, 0, timesync_tick, AUTOSTART_TIMESYNC

View File

@@ -0,0 +1,70 @@
; =============================================================================
; Модуль : Инициализация драйвера VBoxGuest
; Назначение : Инициализация всех подсистем драйвера, подключение к VMMDev,
; настройка сервисов, установка обработчиков
; Файл : sys/init.inc
; =============================================================================
; Полная инициализация драйвера VBoxGuest
proc sys_init
call vmmdev_probe
test eax, eax
jnz .fail
call mmio_map_vmmdev
test eax, eax
jnz .fail
call ports_init ; чекалка
test eax, eax
jnz .fail
call vmmdev_init_packets
test eax, eax
jnz .fail
call vmmdev_check_version ; получить версию хоста (для guest_info2)
call vmmdev_init_protocol ; guest_info + caps + event_filter
test eax, eax
jnz .fail
call hgcm_init ; подготовка пакетов/таймаутов
test eax, eax
jnz .fail
call timer_init
test eax, eax
jnz .fail
call vmmdev_irq_install
test eax, eax
jnz .fail
call dispatcher_init_all
call dispatcher_enable_autostart
call dispatcher_get_active_events
call vmmdev_update_event_filter
test eax, eax
jnz .fail_event_filter
call dispatcher_get_active_caps
call vmmdev_update_capabilities
test eax, eax
jnz .fail_caps
xor eax, eax
ret
.fail_event_filter:
DEBUGF 2, "[VBoxGuest] [Init] Event filter setup failed: 0x%x\n", eax
ret
.fail_caps:
DEBUGF 2, "[VBoxGuest] [Init] Guest capabilities setup failed: 0x%x\n", eax
ret
.fail:
ret
endp

View File

@@ -0,0 +1,183 @@
; =============================================================================
; Модуль : IOCTL интерфейс драйвера VBoxGuest
; Назначение : Обработка команд от приложений (service_proc, ioctl_get_services)
; Файл : sys/ioctl.inc
; =============================================================================
; IOCTL ABI v1
API_VERSION equ 1
; General
VBOX_IOCTL_GET_VERSION equ 0
VBOX_IOCTL_GET_SERVICES equ 1
VBOX_IOCTL_SVC_ENABLE equ 2
VBOX_IOCTL_SVC_DISABLE equ 3
; Clipboard
VBOX_IOCTL_CLIP_STATUS equ 10
VBOX_IOCTL_CLIP_READ equ 11
VBOX_IOCTL_CLIP_WRITE equ 12
; SERVICE_INFO size for GET_SERVICES (dd id + rb name[16] + dd enabled = 24)
SVC_INFO_SIZE equ 24
SVC_INFO_NAME_LEN equ 16
; =============================================================================
; Service Procedure IOCTL dispatch
; =============================================================================
proc service_proc stdcall, ioctl:dword
mov ebx, [ioctl]
mov eax, [ebx + IOCTL.io_code]
cmp eax, VBOX_IOCTL_GET_VERSION
je .get_version
cmp eax, VBOX_IOCTL_GET_SERVICES
je .get_services
cmp eax, VBOX_IOCTL_SVC_ENABLE
je .svc_enable
cmp eax, VBOX_IOCTL_SVC_DISABLE
je .svc_disable
cmp eax, VBOX_IOCTL_CLIP_STATUS
je .clip_status
cmp eax, VBOX_IOCTL_CLIP_READ
je .clip_read
cmp eax, VBOX_IOCTL_CLIP_WRITE
je .clip_write
jmp .fail
; --- GET_VERSION (0) ---
.get_version:
cmp [ebx + IOCTL.out_size], 4
jb .fail
mov eax, [ebx + IOCTL.output]
mov dword [eax], API_VERSION
xor eax, eax
ret
; --- GET_SERVICES (1) ---
.get_services:
stdcall ioctl_get_services, ebx
ret
; --- SVC_ENABLE (2) ---
.svc_enable:
cmp [ebx + IOCTL.inp_size], 4
jb .fail
mov eax, [ebx + IOCTL.input]
mov eax, [eax]
stdcall dispatcher_enable_by_id, eax
push eax
call dispatcher_get_active_events
call vmmdev_update_event_filter
call dispatcher_get_active_caps
call vmmdev_update_capabilities
pop eax
ret
; --- SVC_DISABLE (3) ---
.svc_disable:
cmp [ebx + IOCTL.inp_size], 4
jb .fail
mov eax, [ebx + IOCTL.input]
mov eax, [eax]
stdcall dispatcher_disable_by_id, eax
push eax
call dispatcher_get_active_events
call vmmdev_update_event_filter
call dispatcher_get_active_caps
call vmmdev_update_capabilities
pop eax
ret
; --- CLIP_STATUS (10) ---
.clip_status:
stdcall clip_ioctl_status, ebx
ret
; --- CLIP_READ (11) ---
.clip_read:
stdcall clip_ioctl_read, ebx
ret
; --- CLIP_WRITE (12) ---
.clip_write:
stdcall clip_ioctl_write, ebx
ret
.fail:
or eax, -1
ret
endp
; =============================================================================
; ioctl_get_services Заполнить список сервисов
; =============================================================================
proc ioctl_get_services stdcall uses ebx ecx edx esi edi, ioctl_ptr:dword
mov ebx, [ioctl_ptr]
cmp [ebx + IOCTL.out_size], 4
jb .fail
mov edi, [ebx + IOCTL.output]
mov ecx, [services_count]
mov [edi], ecx ; dd count
add edi, 4
test ecx, ecx
jz .done
; Доступное место = out_size - 4
mov edx, [ebx + IOCTL.out_size]
sub edx, 4
mov esi, services_table
.loop:
cmp edx, SVC_INFO_SIZE
jb .done
; dd id
mov eax, [esi + SERVICE_ENTRY.id]
mov [edi], eax
add edi, 4
; rb name[16] null-padded
push ecx esi edi
mov ecx, SVC_INFO_NAME_LEN
; Заполнить нулями
push edi
xor al, al
rep stosb
pop edi
; Скопировать имя
mov esi, [esi + SERVICE_ENTRY.name_ptr]
mov ecx, SVC_INFO_NAME_LEN - 1
.copy_name:
lodsb
test al, al
jz .name_done
stosb
dec ecx
jnz .copy_name
.name_done:
pop edi esi ecx
add edi, SVC_INFO_NAME_LEN
; dd enabled
mov eax, [esi + SERVICE_ENTRY.enabled]
mov [edi], eax
add edi, 4
sub edx, SVC_INFO_SIZE
add esi, sizeof.SERVICE_ENTRY
dec ecx
jnz .loop
.done:
xor eax, eax
ret
.fail:
or eax, -1
ret
endp

View File

@@ -0,0 +1,47 @@
; =============================================================================
; Модуль : Корректное завершение работы драйвера VBoxGuest
; Назначение : Деинициализация всех подсистем при выгрузке драйвера (DRV_EXIT)
; Файл : sys/shutdown.inc
; Порядок : По образцу Linux vbg_core_exit для избежания зависаний
; =============================================================================
; Полная деинициализация драйвера VBoxGuest (вызывается при DRV_EXIT)
proc sys_shutdown
DEBUGF 2, "[VBoxGuest] Shutdown started\n"
; 1. Остановить все сервисы (отправить уведомления хосту)
call dispatcher_disable_all
DEBUGF 2, "[VBoxGuest] All services disabled\n"
; 2. Остановить таймер (ПЕРЕД IRQ detach, иначе tick вызовется во время cleanup)
call timer_stop
DEBUGF 2, "[VBoxGuest] Timer stopped\n"
; 3. Убрать IRQ handler ОБЯЗАТЕЛЬНО до освобождения буферов
; ISR не должна писать в освобождаемую память
; call vbox_irq_detach
; DEBUGF 2, "[VBoxGuest] IRQ handler detached\n"
; 4. Сбросить маски события и возможностей на хосте (маска = 0)
xor eax, eax
mov [dispatcher_active_events], eax
call vmmdev_update_event_filter
DEBUGF 2, "[VBoxGuest] Event filter cleared\n"
mov [dispatcher_active_caps], eax
call vmmdev_update_capabilities
DEBUGF 2, "[VBoxGuest] Capabilities cleared\n"
; 5. Освободить пакеты (ПОСЛЕ IRQ detach ISR больше не пишет в них)
call hgcm_free_packets
DEBUGF 2, "[VBoxGuest] HGCM packets freed\n"
call vmmdev_free_packets
DEBUGF 2, "[VBoxGuest] VMMDev packets freed\n"
DEBUGF 2, "[VBoxGuest] Shutdown complete\n"
xor eax, eax
ret
endp

View File

@@ -0,0 +1,92 @@
; =============================================================================
; Программа : VirtualBox Guest Driver для KolibriOS
; Модуль : Main driver entry
; Назначение : Full-featured driver
; Файл : vboxguest.asm
; Автор : lex_coder(lex_coder@mail.ru), Алексей Михайлов
; При моральной поддержке сообщества KolibriOS
;
; cp /usbhd0/1/vboxguest/vboxguest /sys/Drivers/vboxguest.sys
; loaddrv vboxguest
;
; =============================================================================
format PE DLL native 0.05
entry START
section '.flat' code readable writable executable
include '/usbhd0/1/ksrc_n/DRIVERS/proc32.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/struct.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/macros.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/peimport.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/fdo.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/pci.inc'
include 'config.inc'
include 'common/common.inc'
include 'data/data.inc'
include 'core/core.inc'
include 'vmmdev/vmmdev.inc'
include 'hgcm/core.inc'
include 'sys/init.inc'
include 'sys/shutdown.inc'
include 'sys/ioctl.inc'
include 'services/services.inc'
BUILD_SERVICE_TABLE
; =============================================================================
; Driver Entry Point
; =============================================================================
proc START c, state:dword, cmdline:dword
push ebx esi edi
cmp [state], DRV_ENTRY
je .entry
cmp [state], DRV_EXIT
je .exit
pop edi esi ebx
xor eax, eax
ret
.entry:
DEBUGF 2, "[VBoxGuest] VBoxGuest Driver v2.0\n"
call sys_init ;
test eax, eax
jnz .fail
; Регистрация сервиса (IOCTL интерфейс для приложений)
invoke RegService, service_name, service_proc
DEBUGF 2, "[VBoxGuest] IOCTL service '%s' registered (API v%d)\n", service_name, API_VERSION
DEBUGF 2, "[VBoxGuest] Initialization complete!\n"
pop edi esi ebx
ret
.fail:
DEBUGF 2, "[VBoxGuest] Initialization FAILED\n"
.exit:
DEBUGF 2, "[VBoxGuest] DRV_EXIT: Shutting down driver\n"
call sys_shutdown
pop edi esi ebx
xor eax, eax
ret
endp
; =============================================================================
; Data Section
; =============================================================================
align 4
service_name db 'VBOXGUEST', 0
align 4
vbox_device VBOX_DEVICE
include_debug_strings
align 4
data fixups
end data

View File

@@ -0,0 +1,27 @@
; =============================================================================
; Модуль : VMMDev Guest Capabilities
; Назначение : Настройка возможностей гостя (REQ 56)
; Файл : vmmdev/capabilities.inc
; =============================================================================
; vmmdev_update_capabilities Обновить caps по runtime-маске из dispatcher
proc vmmdev_update_capabilities
mov edi, [vbox_device.caps_virt]
test edi, edi
jz .bad
; Обновить or_mask в пакете
mov [edi + VMMDEV_SET_GUEST_CAPABILITIES2.or_mask], eax
mov dword [edi + VMMDEV_SET_GUEST_CAPABILITIES2.header.rc], 0
DEBUGF 2, "[VBoxGuest] [Caps] Updating capabilities: or_mask=0x%x\n", eax
mov ebx, [vbox_device.caps_phys]
stdcall vmmdev_send_request, ebx
mov eax, [edi + VMMDEV_SET_GUEST_CAPABILITIES2.header.rc]
ret
.bad:
mov eax, VERR_INVALID_POINTER
ret
endp

View File

@@ -0,0 +1,25 @@
; =============================================================================
; Модуль : VMMDev Core
; Назначение : Базовые операции VMMDev: send_request, init протокола
; Файл : vmmdev/core.inc
; =============================================================================
; Базовая настройка протокола VMMDev
proc vmmdev_init_protocol
; GuestInfo
call guest_info_report
test eax, eax
jnz .fail
; GuestInfo2 (non-fatal не все версии VBox поддерживают)
call guest_info_2_report
test eax, eax
jz @f
DEBUGF 2, "[VBoxGuest] [VMMDev] GuestInfo2 failed (rc=0x%x), continuing...\n", eax
@@:
xor eax, eax
.fail:
ret
endp

View File

@@ -0,0 +1,28 @@
; =============================================================================
; Модуль : VMMDev Event Filter
; Назначение : Фильтр событий VMMDev (REQ 42)
; Файл : vmmdev/event_filter.inc
; =============================================================================
; vmmdev_update_event_filter Обновить фильтр по маске из dispatcher
proc vmmdev_update_event_filter
mov edi, [vbox_device.filter_virt]
test edi, edi
jz .bad
; Обновить or_mask в пакете
mov [edi + VMMDEV_CTL_GUEST_FILTER_MASK.or_mask], eax
mov dword [edi + VMMDEV_CTL_GUEST_FILTER_MASK.header.rc], 0
DEBUGF 2, "[VBoxGuest] [Filter] Updating event filter: or_mask=0x%x\n", eax
mov ebx, [vbox_device.filter_phys]
stdcall vmmdev_send_request, ebx
mov eax, [edi + VMMDEV_CTL_GUEST_FILTER_MASK.header.rc]
ret
.bad:
mov eax, VERR_INVALID_POINTER
ret
endp

View File

@@ -0,0 +1,77 @@
; =============================================================================
; Модуль : VMMDev Guest Info
; Назначение : ReportGuestInfo / ReportGuestInfo2
; Файл : vmmdev/guest_info.inc
; =============================================================================
; Отправка ReportGuestInfo (REQ 50)
proc guest_info_report uses ebx esi
mov esi, [vbox_device.guestinfo_virt]
mov ebx, [vbox_device.guestinfo_phys]
test esi, esi
jz .bad
test ebx, ebx
jz .bad
stdcall vmmdev_send_request, ebx
mov eax, [esi + VMMDEV_HEADER.rc]
test eax, eax
jnz .error
DEBUGF 2, "[VBoxGuest] [VMMDev] Guest info sent (interface 0x%x)\n", VMMDEV_VERSION
xor eax, eax
ret
.error:
DEBUGF 2, "[VBoxGuest] [VMMDev] send_guest_info failed, rc=0x%x\n", eax
mov eax, VERR_GENERAL_FAILURE
ret
.bad:
mov eax, VERR_INVALID_PARAMETER
ret
endp
; Отправка ReportGuestInfo2 (REQ 58), версия заполняется из host_version
proc guest_info_2_report uses ebx esi edi
mov esi, [vbox_device.guestinfo2_virt]
mov ebx, [vbox_device.guestinfo2_phys]
test esi, esi
jz .bad
test ebx, ebx
jz .bad
; Заполнить версию GA из полученной версии хоста
mov edi, [vbox_device.host_version_virt]
test edi, edi
jz .send ; нет данных отправляем как есть (нули)
movzx eax, word [edi + VMMDEV_GET_HOST_VERSION.major]
mov word [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_major], ax
movzx eax, word [edi + VMMDEV_GET_HOST_VERSION.minor]
mov word [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_minor], ax
mov eax, [edi + VMMDEV_GET_HOST_VERSION.build]
mov dword [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_build], eax
mov eax, [edi + VMMDEV_GET_HOST_VERSION.revision]
mov dword [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_revision], eax
.send:
stdcall vmmdev_send_request, ebx
mov eax, [esi + VMMDEV_HEADER.rc]
test eax, eax
jnz .error
DEBUGF 2, "[VBoxGuest] [VMMDev] Guest info 2 sent (interface 0x%x)\n", VMMDEV_VERSION
xor eax, eax
ret
.error:
DEBUGF 2, "[VBoxGuest] [VMMDev] send_guest_info_2 failed, rc=0x%x\n", eax
mov eax, VERR_GENERAL_FAILURE
ret
.bad:
mov eax, VERR_INVALID_PARAMETER
ret
endp

View File

@@ -0,0 +1,51 @@
; =============================================================================
; Модуль : VMMDev Host/Hypervisor Info
; Назначение : Получение информации о хосте (REQ 4: GetHostVersion)
; Файл : vmmdev/hypervisor.inc
; =============================================================================
; Отправить REQ 4 GetHostVersion, eax = header.rc
proc vmmdev_get_host_version uses ebx edi
mov edi, [vbox_device.host_version_virt]
mov ebx, [vbox_device.host_version_phys]
test edi, edi
jz .bad
test ebx, ebx
jz .bad
stdcall vmmdev_send_request, ebx
mov eax, [edi + VMMDEV_HEADER.rc]
ret
.bad:
mov eax, VERR_INVALID_POINTER
ret
endp
proc vmmdev_check_version uses ebx
DEBUGF 2, "[VBoxGuest] [VMMDev] Checking host version (GetHostVersion)...\n"
stdcall vmmdev_get_host_version
DEBUGF 2, "[VBoxGuest] [VMMDev] GetHostVersion rc=0x%x\n", eax
test eax, eax
jnz .error
mov eax, [vbox_device.host_version_virt]
movzx ebx, [eax + VMMDEV_GET_HOST_VERSION.major]
movzx ecx, [eax + VMMDEV_GET_HOST_VERSION.minor]
DEBUGF 2, "[VBoxGuest] [VMMDev] Host version: %d.%d.%d r%d features=0x%x\n", \
ebx, ecx, \
[eax + VMMDEV_GET_HOST_VERSION.build], \
[eax + VMMDEV_GET_HOST_VERSION.revision], \
[eax + VMMDEV_GET_HOST_VERSION.features]
xor eax, eax
ret
.error:
DEBUGF 2, "[VBoxGuest] [VMMDev] ERROR: GetHostVersion failed, rc=0x%x\n", eax
mov eax, VERR_GENERAL_FAILURE
ret
endp

View File

@@ -0,0 +1,169 @@
; =============================================================================
; Модуль : VMMDev Packets
; Назначение : Выделение и инициализация пакетов VMMDev (одна страница 4KB)
; Файл : vmmdev/packets.inc
; =============================================================================
VMMDEV_PKT_OFS_HOST_VERSION = 0 ; sizeof.VMMDEV_GET_HOST_VERSION
VMMDEV_PKT_OFS_GUEST_INFO = 64 ; sizeof.VMMDEV_REPORT_GUEST_INFO
VMMDEV_PKT_OFS_GUEST_INFO2 = 128 ; sizeof.VMMDEV_REPORT_GUEST_INFO2 (~168)
VMMDEV_PKT_OFS_CAPS = 320 ; sizeof.VMMDEV_SET_GUEST_CAPABILITIES2
VMMDEV_PKT_OFS_FILTER = 384 ; sizeof.VMMDEV_CTL_GUEST_FILTER_MASK
VMMDEV_PKT_OFS_ACK_EVENTS = 448 ; sizeof.VMMDEV_ACKNOWLEDGE_EVENTS
; Строка версии (read-only, для копирования в пакет)
vmmdev_version_string db '1.0.0_KolibriOS', 0
VMMDEV_VERSION_STRING_LEN = $ - vmmdev_version_string
; =============================================================================
; vmmdev_init_packets Выделить страницу и инициализировать все VMMDev пакеты
; =============================================================================
proc vmmdev_init_packets uses ebx ecx edx esi edi
DEBUGF 2, "[VBoxGuest] [VMMDev] Init packets (dynamic)\n"
; --- Выделить страницу ---
invoke KernelAlloc, 4096
test eax, eax
jz .alloc_fail
mov [vbox_device.vmmdev_packets_page], eax
; Обнулить страницу
mov edi, eax
xor eax, eax
mov ecx, 4096 / 4
rep stosd
mov ebx, [vbox_device.vmmdev_packets_page]
; =================================================================
; 1) HOST_VERSION (offset 0)
; =================================================================
lea edi, [ebx + VMMDEV_PKT_OFS_HOST_VERSION]
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_GET_HOST_VERSION
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_GET_HOST_VERSION
mov [vbox_device.host_version_virt], edi
mov eax, edi
invoke GetPhysAddr
mov [vbox_device.host_version_phys], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] HostVersion packet: virt=0x%x, phys=0x%x\n", \
[vbox_device.host_version_virt], eax
; =================================================================
; 2) REPORT_GUEST_INFO (offset 64)
; =================================================================
lea edi, [ebx + VMMDEV_PKT_OFS_GUEST_INFO]
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_REPORT_GUEST_INFO
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_REPORT_GUEST_INFO
mov dword [edi + VMMDEV_REPORT_GUEST_INFO.interface_version], VMMDEV_VERSION
mov dword [edi + VMMDEV_REPORT_GUEST_INFO.os_type], VBOXOSTYPE_KOLIBRIOS
mov [vbox_device.guestinfo_virt], edi
mov eax, edi
invoke GetPhysAddr
mov [vbox_device.guestinfo_phys], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] GuestInfo packet : virt=0x%x, phys=0x%x\n", \
[vbox_device.guestinfo_virt], eax
; =================================================================
; 3) REPORT_GUEST_INFO2 (offset 128)
; =================================================================
lea edi, [ebx + VMMDEV_PKT_OFS_GUEST_INFO2]
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_REPORT_GUEST_INFO2
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_REPORT_GUEST_INFO2
; rc, reserved1, f_requestor уже 0 (обнулено)
; payload: версия заполняется из host_version в guest_info_2_report()
mov dword [edi + VMMDEV_REPORT_GUEST_INFO2.guest_info + VMMDEV_GUEST_INFO2.additions_features], VBOXGSTINFO2_F_REQUESTOR_INFO
; szName[128] копируем строку версии
push edi ecx
lea edi, [edi + VMMDEV_REPORT_GUEST_INFO2.guest_info + VMMDEV_GUEST_INFO2.szName]
mov esi, vmmdev_version_string
mov ecx, VMMDEV_VERSION_STRING_LEN
rep movsb
pop ecx edi
lea eax, [ebx + VMMDEV_PKT_OFS_GUEST_INFO2]
mov [vbox_device.guestinfo2_virt], eax
invoke GetPhysAddr
mov [vbox_device.guestinfo2_phys], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] GuestInfo2 packet : virt=0x%x, phys=0x%x\n", \
[vbox_device.guestinfo2_virt], eax
; =================================================================
; 4) SET_GUEST_CAPABILITIES2 (offset 320)
; =================================================================
lea edi, [ebx + VMMDEV_PKT_OFS_CAPS]
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_SET_GUEST_CAPABILITIES2
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_SET_GUEST_CAPS
mov dword [edi + VMMDEV_SET_GUEST_CAPABILITIES2.or_mask], VBOXGUEST_GUEST_CAPS_OR_MASK
mov dword [edi + VMMDEV_SET_GUEST_CAPABILITIES2.not_mask], VBOXGUEST_GUEST_CAPS_NOT_MASK
mov [vbox_device.caps_virt], edi
mov eax, edi
invoke GetPhysAddr
mov [vbox_device.caps_phys], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] Caps packet : virt=0x%x, phys=0x%x\n", \
[vbox_device.caps_virt], eax
; =================================================================
; 5) CTL_GUEST_FILTER_MASK (offset 384)
; =================================================================
lea edi, [ebx + VMMDEV_PKT_OFS_FILTER]
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_CTL_GUEST_FILTER_MASK
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_CTL_GUEST_FILTER_MASK
mov dword [edi + VMMDEV_CTL_GUEST_FILTER_MASK.or_mask], VBOXGUEST_EVENTS_OR_MASK
mov dword [edi + VMMDEV_CTL_GUEST_FILTER_MASK.not_mask], VBOXGUEST_EVENTS_NOT_MASK
mov [vbox_device.filter_virt], edi
mov eax, edi
invoke GetPhysAddr
mov [vbox_device.filter_phys], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] Filter packet : virt=0x%x, phys=0x%x\n", \
[vbox_device.filter_virt], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] Filter: OR=0x%x, NOT=0x%x\n", \
[edi + VMMDEV_CTL_GUEST_FILTER_MASK.or_mask], \
[edi + VMMDEV_CTL_GUEST_FILTER_MASK.not_mask]
; =================================================================
; 6) ACKNOWLEDGE_EVENTS (offset 448)
; =================================================================
lea edi, [ebx + VMMDEV_PKT_OFS_ACK_EVENTS]
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_ACKNOWLEDGE_EVENTS
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_ACKNOWLEDGE_EVENTS
mov [vbox_device.events_virt], edi
mov eax, edi
invoke GetPhysAddr
mov [vbox_device.events_phys], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] ACK packet : virt=0x%x, phys=0x%x\n", \
[vbox_device.events_virt], eax
DEBUGF 2, "[VBoxGuest] [VMMDev] Init packets success\n"
xor eax, eax
ret
.alloc_fail:
DEBUGF 2, "[VBoxGuest] [VMMDev] ERROR: KernelAlloc failed for VMMDev packets\n"
mov eax, VERR_NO_MEMORY
ret
endp
; =============================================================================
; vmmdev_free_packets Освободить страницу VMMDev пакетов
; =============================================================================
proc vmmdev_free_packets
cmp dword [vbox_device.vmmdev_packets_page], 0
je @f
invoke KernelFree, [vbox_device.vmmdev_packets_page]
mov dword [vbox_device.vmmdev_packets_page], 0
@@:
ret
endp

View File

@@ -0,0 +1,12 @@
; =============================================================================
; Модуль : VMMDev Aggregator
; Назначение : Подключение модулей протокола VMMDev
; Файл : vmmdev/vmmdev.inc
; =============================================================================
include 'vmmdev/packets.inc'
include 'vmmdev/core.inc'
include 'vmmdev/guest_info.inc'
include 'vmmdev/capabilities.inc'
include 'vmmdev/event_filter.inc'
include 'vmmdev/hypervisor.inc'