VBoxGuest driver and control

This commit is contained in:
lex
2026-03-04 22:03:47 +03:00
parent 0f400bc0e0
commit 53f047232f
75 changed files with 12248 additions and 4033 deletions

6
.gitignore vendored
View File

@@ -1,6 +0,0 @@
_
*.txt
vboxguest.sublime-project
vboxguest.sublime-workspace
~*

View File

2
common/common.inc Normal file
View File

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

View File

@@ -1,149 +0,0 @@
; PCI Device IDs
VBOX_VENDOR_ID equ 0x80EE
VBOX_DEVICE_ID equ 0xCAFE
; VMMDev Protocol
VMMDEV_REQUEST_HEADER_VERSION equ 0x10001
VMMDEV_VERSION equ 0x00010003
VMMDEV_MEM_SIZE equ 0x00400000
VMMDEV_REQ_ACKNOWLEDGE_EVENTS equ 41
VMMDEV_REQ_REPORT_GUEST_INFO equ 50
VMMDEV_REQ_GET_DISPLAY_CHANGE equ 51
VMMDEV_REQ_SET_GUEST_CAPS equ 55
; VMMDev Events
; VMMDEV_EVENT_HGCM equ 0x00000002
; VMMDEV_EVENT_DISPLAY_CHANGE equ 0x00000004
; Guest Capabilities & OS Type
VBOXOSTYPE_KOLIBRIOS equ 0x90000
; Guest Capabilities
VMMDEV_GUEST_SUPPORTS_SEAMLESS equ 0x00000001
VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_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_VRDP_RESIZE equ 0x00000100
VMMDEV_GUEST_SUPPORTS_DRAG_AND_DROP equ 0x00000200
VMMDEV_GUEST_SUPPORTS_CR3_MONITORING equ 0x00000400
; Full capabilities set
VBOXGUEST_OS_SUPPORTS_CAPS equ (VMMDEV_GUEST_SUPPORTS_GRAPHICS or VMMDEV_GUEST_SUPPORTS_HGCM or VMMDEV_GUEST_SUPPORTS_DRAG_AND_DROP)
; HGCM Protocol
HGCM_PARM_TYPE_INVALID equ 0
HGCM_PARM_TYPE_32BIT equ 1
HGCM_PARM_TYPE_64BIT equ 2
HGCM_PARM_TYPE_PHYSADDR equ 3 ; deprecated
HGCM_PARM_TYPE_LINADDR equ 4
HGCM_PARM_TYPE_LINADDR_IN equ 5
HGCM_PARM_TYPE_LINADDR_OUT equ 6
HGCM_PARM_TYPE_LINADDR_LOCKED equ 7 ; НЕ ИСПОЛЬЗОВАТЬ
HGCM_PARM_TYPE_LINADDR_LOCKED_IN equ 8 ; НЕ ИСПОЛЬЗОВАТЬ
HGCM_PARM_TYPE_LINADDR_LOCKED_OUT equ 9 ; НЕ ИСПОЛЬЗОВАТЬ
HGCM_PARM_TYPE_PAGELIST equ 10
VMMDEV_HGCM_CONNECT equ 60
VMMDEV_HGCM_DISCONNECT equ 61
VMMDEV_HGCM_CALL32 equ 62
VBOX_HGCM_REQ_DONE equ 0x00000001
HGCM_LOC_TYPE_PREDEFINED equ 2
HGCM_SERVICE_NAME_MAX equ 128
HGCM_TIMEOUT_DEFAULT equ 500000
PAGE_SHIFT equ 12
; Clipboard Constants
VBOX_SHCL_GUEST_FN_MSG_GET equ 1
VBOX_SHCL_GUEST_FN_FORMATS_REPORT equ 2
VBOX_SHCL_GUEST_FN_DATA_READ equ 3
VBOX_SHCL_GUEST_FN_DATA_WRITE equ 4
VBOX_SHCL_HOST_MSG_FORMATS_REPORT equ 0
VBOX_SHCL_HOST_MSG_READ_DATA equ 1
VBOX_SHCL_FMT_UNICODETEXT equ 0x0001
VBOX_SHCL_FMT_BITMAP equ 0x0002
VBOX_SHCL_FMT_HTML equ 0x0004
VBOX_SHCL_MAX_CHUNK_SIZE equ 0x10000
; Display/VGA Constants
VGA_INDEX_PORT equ 0x01CE
VGA_DATA_PORT equ 0x01CF
VGA_INDEX_ENABLE equ 0x4
VGA_INDEX_XRES equ 0x1
VGA_INDEX_YRES equ 0x2
VGA_INDEX_BPP equ 0x3
VGA_DISABLED equ 0x00
VGA_ENABLED equ 0x01
VGA_LFB_ENABLED equ 0x40
DISP_W_MIN equ 640
DISP_H_MIN equ 480
DISP_W_MAX equ 3840
DISP_H_MAX equ 2160
; =============================================================================
; SharedFolder
; =============================================================================
; HGCM функции для SharedFolder сервиса
SHFL_FN_QUERY_MAPPINGS equ 1 ; Получить список mappings
SHFL_FN_QUERY_MAP_NAME equ 2 ; Получить имя mapping
SHFL_FN_CREATE equ 5 ; Открыть/создать файл
SHFL_FN_CLOSE equ 6 ; Закрыть файл
SHFL_FN_READ equ 7 ; Читать из файла
SHFL_FN_WRITE equ 8 ; Писать в файл
SHFL_FN_LIST equ 9 ; Список файлов в директории
SHFL_FN_READLINK equ 10 ; Чтение symlink
SHFL_FN_INFORMATION equ 11 ; Информация о файле
SHFL_FN_REMOVE equ 13 ; Удалить файл
SHFL_FN_MAP_FOLDER equ 14 ; Подключить mapping
SHFL_FN_UNMAP_FOLDER equ 15 ; Отключить mapping
SHFL_FN_RENAME equ 16 ; Переименовать
SHFL_FN_FLUSH equ 17 ; Flush буферов
; Флаги создания/открытия файла
SHFL_CF_LOOKUP equ 0x00000001 ; Только lookup (не создавать)
SHFL_CF_DIRECTORY equ 0x00000004 ; Это директория
SHFL_CF_ACCESS_READ equ 0x00000001 ; Чтение
SHFL_CF_ACCESS_WRITE equ 0x00000002 ; Запись
SHFL_CF_ACCESS_READWRITE equ 0x00000003 ; Чтение + запись
SHFL_CF_ACCESS_ALL equ 0x00000003
SHFL_CF_ACCESS_ATTR_READ equ 0x00000004 ; Чтение атрибутов
SHFL_CF_ACCESS_ATTR_WRITE equ 0x00000008 ; Запись атрибутов
SHFL_CF_ACCESS_MASK equ 0x0000000F
; Результаты операции создания
SHFL_FILE_EXISTS equ 0 ; Файл уже существует
SHFL_FILE_CREATED equ 1 ; Файл создан
SHFL_FILE_REPLACED equ 2 ; Файл заменен
SHFL_PATH_NOT_FOUND equ 3 ; Путь не найден
; Флаги для LIST операции
SHFL_LIST_NONE equ 0x00000000
SHFL_LIST_RETURN_ONE equ 0x00000001 ; Вернуть только один элемент
; Максимальные размеры
SHFL_MAX_MAPPINGS equ 10 ; Максимум mappings
SHFL_MAX_NAME_LEN equ 256 ; Максимум длина имени
SHFL_MAX_PATH_LEN equ 4096; Максимум длина пути
; =============================================================================
; KolibriOS Disk Status Codes
; =============================================================================
DISK_STATUS_GENERAL_ERROR equ -1
DISK_STATUS_OK equ 0
DISK_STATUS_INVALID_CALL equ 1
DISK_STATUS_NO_MEDIA equ 2
DISK_STATUS_END_OF_MEDIA equ 3
; =============================================================================
; Clipboard FSM States
; =============================================================================
CLIPBOARD_STATE_INIT equ 0 ; Not initialized
CLIPBOARD_STATE_CONNECTING equ 1 ; HGCM connect in progress
CLIPBOARD_STATE_READY equ 2 ; Ready to process events

View File

@@ -1,459 +0,0 @@
; =============================================================================
; Encoding Conversion - UTF-8 UTF-16LE
; Based on Linux VirtualBox Guest Additions implementation
; =============================================================================
; =============================================================================
; ПРОЦЕДУРА: utf8_to_utf16le
; Конвертация UTF-8 в UTF-16LE (как требует VirtualBox)
; Вход:
; utf8_ptr - указатель на UTF-8 строку
; utf8_len - длина UTF-8 строки в байтах (без null-terminator)
; utf16_ptr - указатель на выходной буфер UTF-16LE
; utf16_max - максимальный размер выходного буфера в байтах
; Возврат:
; EAX = количество байт записанных в UTF-16LE (включая BOM если есть)
; или -1 при ошибке
; =============================================================================
proc utf8_to_utf16le uses ebx ecx edx esi edi, \
utf8_ptr:dword, utf8_len:dword, utf16_ptr:dword, utf16_max:dword
DEBUGF 2, "[enc] >>> utf8_to_utf16le(src=0x%x, len=%d, dst=0x%x, max=%d)\n", \
[utf8_ptr], [utf8_len], [utf16_ptr], [utf16_max]
mov esi, [utf8_ptr]
test esi, esi
jz .error
mov edi, [utf16_ptr]
test edi, edi
jz .error
mov ecx, [utf8_len]
test ecx, ecx
jz .add_null
mov edx, [utf16_max]
cmp edx, 4 ; Минимум для BOM + null
jb .error
push edi ; Сохраним начало буфера
; Добавляем UTF-16LE BOM (0xFEFF)
mov word [edi], 0xFEFF
add edi, 2
sub edx, 2
.loop:
test ecx, ecx
jz .add_null
cmp edx, 4 ; Нужно место для символа + null
jb .buffer_full
; Читаем первый байт UTF-8
movzx eax, byte [esi]
inc esi
dec ecx
; Проверяем тип символа
cmp al, 0x80
jb .ascii ; 0xxxxxxx - ASCII (1 байт)
cmp al, 0xE0
jb .two_byte ; 110xxxxx - 2 байта
cmp al, 0xF0
jb .three_byte ; 1110xxxx - 3 байта
cmp al, 0xF8
jb .four_byte ; 11110xxx - 4 байта
; Неправильный UTF-8 - заменяем на replacement character
mov word [edi], 0xFFFD
add edi, 2
sub edx, 2
jmp .loop
.ascii:
; ASCII символ - прямая конвертация
mov word [edi], ax
add edi, 2
sub edx, 2
jmp .loop
.two_byte:
; 110xxxxx 10xxxxxx
test ecx, ecx
jz .incomplete
and eax, 0x1F ; Берем 5 бит
shl eax, 6
movzx ebx, byte [esi]
inc esi
dec ecx
and ebx, 0x3F ; Берем 6 бит
or eax, ebx
mov word [edi], ax
add edi, 2
sub edx, 2
jmp .loop
.three_byte:
; 1110xxxx 10xxxxxx 10xxxxxx
cmp ecx, 2
jb .incomplete
and eax, 0x0F ; Берем 4 бита
shl eax, 12
movzx ebx, byte [esi]
inc esi
dec ecx
and ebx, 0x3F
shl ebx, 6
or eax, ebx
movzx ebx, byte [esi]
inc esi
dec ecx
and ebx, 0x3F
or eax, ebx
mov word [edi], ax
add edi, 2
sub edx, 2
jmp .loop
.four_byte:
; 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
; Это символ вне BMP - нужна surrogate pair
cmp ecx, 3
jb .incomplete
cmp edx, 6 ; Нужно 4 байта для surrogate pair + null
jb .buffer_full
and eax, 0x07 ; Берем 3 бита
shl eax, 18
movzx ebx, byte [esi]
inc esi
dec ecx
and ebx, 0x3F
shl ebx, 12
or eax, ebx
movzx ebx, byte [esi]
inc esi
dec ecx
and ebx, 0x3F
shl ebx, 6
or eax, ebx
movzx ebx, byte [esi]
inc esi
dec ecx
and ebx, 0x3F
or eax, ebx
; Преобразуем в surrogate pair
sub eax, 0x10000
; High surrogate: 0xD800 + (code >> 10)
mov ebx, eax
shr ebx, 10
add ebx, 0xD800
mov word [edi], bx
add edi, 2
sub edx, 2
; Low surrogate: 0xDC00 + (code & 0x3FF)
and eax, 0x3FF
add eax, 0xDC00
mov word [edi], ax
add edi, 2
sub edx, 2
jmp .loop
.incomplete:
; Неполная последовательность - заменяем на replacement
mov word [edi], 0xFFFD
add edi, 2
sub edx, 2
jmp .add_null
.add_null:
; Добавляем null-terminator
cmp edx, 2
jb .buffer_full
mov word [edi], 0
add edi, 2
; Вычисляем размер
pop eax ; Начало буфера
sub edi, eax
mov eax, edi
DEBUGF 2, "[enc] Converted: %d bytes UTF-16LE (with BOM)\n", eax
ret
.buffer_full:
pop eax ; Cleanup stack
DEBUGF 1, "[enc] ERROR: Output buffer too small\n"
mov eax, -1
ret
.error:
DEBUGF 1, "[enc] ERROR: Invalid parameters\n"
mov eax, -1
ret
endp
; =============================================================================
; ПРОЦЕДУРА: utf16le_to_utf8
; Конвертация UTF-16LE в UTF-8
; Вход:
; utf16_ptr - указатель на UTF-16LE строку
; utf16_len - длина UTF-16LE в байтах (включая BOM если есть)
; utf8_ptr - указатель на выходной буфер UTF-8
; utf8_max - максимальный размер выходного буфера в байтах
; Возврат:
; EAX = количество байт записанных в UTF-8 (без null-terminator)
; или -1 при ошибке
; =============================================================================
proc utf16le_to_utf8 uses ebx ecx edx esi edi, \
utf16_ptr:dword, utf16_len:dword, utf8_ptr:dword, utf8_max:dword
DEBUGF 2, "[enc] >>> utf16le_to_utf8(src=0x%x, len=%d, dst=0x%x, max=%d)\n", \
[utf16_ptr], [utf16_len], [utf8_ptr], [utf8_max]
mov esi, [utf16_ptr]
test esi, esi
jz .error
mov edi, [utf8_ptr]
test edi, edi
jz .error
mov ecx, [utf16_len]
test ecx, ecx
jz .add_null
; Длина должна быть четной
test ecx, 1
jnz .error
mov edx, [utf8_max]
test edx, edx
jz .error
push edi ; Сохраним начало буфера
; Проверяем и пропускаем BOM если есть
cmp word [esi], 0xFEFF
jne .no_bom
add esi, 2
sub ecx, 2
.no_bom:
shr ecx, 1 ; Конвертируем байты в UTF-16 символы
.loop:
test ecx, ecx
jz .add_null
cmp edx, 4 ; Минимум для UTF-8 символа (макс 4 байта)
jb .buffer_full
; Читаем UTF-16 символ (little-endian)
movzx eax, word [esi]
add esi, 2
dec ecx
; Проверяем является ли это high surrogate
cmp ax, 0xD800
jb .not_surrogate
cmp ax, 0xDBFF
ja .check_low_surrogate
; High surrogate - нужна low surrogate
test ecx, ecx
jz .incomplete
movzx ebx, word [esi]
add esi, 2
dec ecx
; Проверяем low surrogate
cmp bx, 0xDC00
jb .invalid_surrogate
cmp bx, 0xDFFF
ja .invalid_surrogate
; Комбинируем surrogates
sub eax, 0xD800
shl eax, 10
sub ebx, 0xDC00
add eax, ebx
add eax, 0x10000
jmp .encode_utf8
.check_low_surrogate:
cmp ax, 0xDC00
jb .not_surrogate
cmp ax, 0xDFFF
ja .not_surrogate
; Одиночная low surrogate - ошибка
jmp .invalid_surrogate
.not_surrogate:
; Обычный BMP символ
.encode_utf8:
; EAX содержит Unicode code point
cmp eax, 0x80
jb .utf8_1byte
cmp eax, 0x800
jb .utf8_2byte
cmp eax, 0x10000
jb .utf8_3byte
; 4-byte UTF-8
cmp edx, 4
jb .buffer_full
mov ebx, eax
shr ebx, 18
and ebx, 0x07
or bl, 0xF0
mov byte [edi], bl
inc edi
dec edx
mov ebx, eax
shr ebx, 12
and ebx, 0x3F
or bl, 0x80
mov byte [edi], bl
inc edi
dec edx
mov ebx, eax
shr ebx, 6
and ebx, 0x3F
or bl, 0x80
mov byte [edi], bl
inc edi
dec edx
and eax, 0x3F
or al, 0x80
mov byte [edi], al
inc edi
dec edx
jmp .loop
.utf8_3byte:
cmp edx, 3
jb .buffer_full
mov ebx, eax
shr ebx, 12
and ebx, 0x0F
or bl, 0xE0
mov byte [edi], bl
inc edi
dec edx
mov ebx, eax
shr ebx, 6
and ebx, 0x3F
or bl, 0x80
mov byte [edi], bl
inc edi
dec edx
and eax, 0x3F
or al, 0x80
mov byte [edi], al
inc edi
dec edx
jmp .loop
.utf8_2byte:
cmp edx, 2
jb .buffer_full
mov ebx, eax
shr ebx, 6
and ebx, 0x1F
or bl, 0xC0
mov byte [edi], bl
inc edi
dec edx
and eax, 0x3F
or al, 0x80
mov byte [edi], al
inc edi
dec edx
jmp .loop
.utf8_1byte:
; ASCII
mov byte [edi], al
inc edi
dec edx
jmp .loop
.invalid_surrogate:
.incomplete:
; Замена на replacement character (U+FFFD = EF BF BD в UTF-8)
cmp edx, 3
jb .buffer_full
mov byte [edi], 0xEF
mov byte [edi+1], 0xBF
mov byte [edi+2], 0xBD
add edi, 3
sub edx, 3
jmp .loop
.add_null:
; Добавляем null-terminator
cmp edx, 1
jb .buffer_full
mov byte [edi], 0
inc edi
; Вычисляем размер (без null)
pop eax ; Начало буфера
sub edi, eax
dec edi ; Не считаем null
mov eax, edi
DEBUGF 2, "[enc] Converted: %d bytes UTF-8\n", eax
ret
.buffer_full:
pop eax ; Cleanup stack
DEBUGF 1, "[enc] ERROR: Output buffer too small\n"
mov eax, -1
ret
.error:
DEBUGF 1, "[enc] ERROR: Invalid parameters\n"
mov eax, -1
ret
endp

View File

@@ -1,272 +1,82 @@
; ============================================================================= ; =============================================================================
; VBoxGuest Driver для KolibriOS ; VBoxGuest Driver for KolibriOS - Dual-level Error Handling
; Модуль : Коды ошибок и обработка ошибок ; VMMDev transport errors + HGCM service errors
; Назначение : Определения кодов ошибок VirtualBox и функции их обработки ; На базе версии v1_04
; Источник : VBox/err.h, VBoxVideoErr.h
; ============================================================================= ; =============================================================================
; ============================================================================= ; =============================================================================
; Коды успеха (>= 0) ; VMMDev Error Codes (Generic VERR_*)
; ============================================================================= ; These appear in vmmdev_request_header.rc
VINF_SUCCESS equ 0 ; Успешное выполнение
VINF_HGCM_ASYNC_EXECUTE equ 2903 ; HGCM выполняется асинхронно
; =============================================================================
; Общие коды ошибок (транспортный уровень VMMDev)
; Диапазон: -1 .. -999
; =============================================================================
VERR_GENERAL_FAILURE equ -1 ; Общая ошибка
VERR_INVALID_PARAMETER equ -2 ; Неверный параметр
VERR_INVALID_MAGIC equ -3 ; Неверная сигнатура
VERR_INVALID_HANDLE equ -4 ; Неверный handle
VERR_LOCK_FAILED equ -5 ; Ошибка блокировки
VERR_INVALID_POINTER equ -6 ; Неверный указатель
VERR_IDT_FAILED equ -7 ; Ошибка IDT
VERR_NO_MEMORY equ -8 ; Недостаточно памяти
VERR_ALREADY_LOADED equ -9 ; Уже загружен
VERR_PERMISSION_DENIED equ -10 ; Доступ запрещён
VERR_TIMEOUT equ -11 ; Таймаут операции
VERR_NOT_IMPLEMENTED equ -12 ; Не реализовано
VERR_NOT_EQUAL equ -18 ; Не равно
VERR_INTERRUPTED equ -39 ; Операция прервана
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_RESOURCE_BUSY equ -138 ; Ресурс занят (0xFFFFFF76)
VERR_INTERNAL_ERROR equ -225 ; Внутренняя ошибка
VERR_INVALID_FUNCTION equ -36 ; Неверная функция
VERR_NOT_SUPPORTED equ -37 ; Не поддерживается
VERR_ACCESS_DENIED equ -38 ; Доступ запрещён
; =============================================================================
; Коды ошибок HGCM (уровень сервисов)
; Диапазон: -2900 .. -2999
; =============================================================================
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 ; Внутренняя ошибка HGCM
VERR_HGCM_INVALID_CLIENT_ID equ -2905 ; Неверный client ID
VERR_HGCM_PROTOCOL_ERROR equ -2906 ; Ошибка протокола
VERR_HGCM_TOO_MANY_CLIENTS equ -2908 ; Слишком много клиентов
VERR_HGCM_TOO_MANY_PARMS equ -2909 ; Слишком много параметров
; =============================================================================
; Коды ошибок Clipboard
; Диапазон: -7150 .. -7199
; =============================================================================
VERR_SHCLPB_NO_DATA equ -7153 ; Нет данных в буфере обмена
VERR_SHCLPB_FORMAT_NOT_SUPPORTED equ -7154 ; Формат не поддерживается
; =============================================================================
; Строки сообщений об ошибках
; =============================================================================
align 4
; Успех
msg_success db "Success", 0
msg_async_pending db "Async operation pending", 0
; Общие ошибки
msg_general_failure db "General failure", 0
msg_invalid_parameter db "Invalid parameter", 0
msg_invalid_pointer db "Invalid pointer", 0
msg_no_memory db "Out of memory", 0
msg_timeout db "Operation timed out", 0
msg_not_implemented db "Not implemented", 0
msg_invalid_function db "Invalid function", 0
msg_not_supported db "Not supported", 0
msg_access_denied db "Access denied", 0
msg_too_much_data db "Too much data", 0
msg_not_found db "Not found", 0
msg_invalid_state db "Invalid state", 0
msg_out_of_resources db "Out of resources", 0
msg_already_exists db "Already exists", 0
msg_internal_error db "Internal error", 0
msg_interrupted db "Operation interrupted", 0
msg_resource_busy db "Resource busy", 0
; Ошибки HGCM
msg_hgcm_service_not_found db "HGCM service not found", 0
msg_hgcm_client_rejected db "HGCM client rejected", 0
msg_hgcm_invalid_cmd db "Invalid HGCM command address", 0
msg_hgcm_internal db "HGCM internal error", 0
msg_hgcm_invalid_client db "Invalid HGCM client ID", 0
msg_hgcm_protocol_error db "HGCM protocol error", 0
msg_hgcm_too_many_clients db "Too many HGCM clients", 0
msg_hgcm_too_many_parms db "Too many HGCM parameters", 0
; Ошибки Clipboard
msg_shclpb_no_data db "No clipboard data", 0
msg_shclpb_format_not_supported db "Clipboard format not supported", 0
; Неизвестная ошибка
msg_unknown_error db "Unknown error", 0
; =============================================================================
; Таблица соответствия кодов ошибок и сообщений
; =============================================================================
struct ERROR_ENTRY
code dd ? ; Код ошибки
message dd ? ; Указатель на строку сообщения
ends
align 4
error_table:
; Коды успеха
dd VINF_SUCCESS, msg_success
dd VINF_HGCM_ASYNC_EXECUTE, msg_async_pending
; Общие ошибки
dd VERR_GENERAL_FAILURE, msg_general_failure
dd VERR_INVALID_PARAMETER, msg_invalid_parameter
dd VERR_INVALID_POINTER, msg_invalid_pointer
dd VERR_NO_MEMORY, msg_no_memory
dd VERR_TIMEOUT, msg_timeout
dd VERR_NOT_IMPLEMENTED, msg_not_implemented
dd VERR_INVALID_FUNCTION, msg_invalid_function
dd VERR_NOT_SUPPORTED, msg_not_supported
dd VERR_ACCESS_DENIED, msg_access_denied
dd VERR_TOO_MUCH_DATA, msg_too_much_data
dd VERR_NOT_FOUND, msg_not_found
dd VERR_INVALID_STATE, msg_invalid_state
dd VERR_OUT_OF_RESOURCES, msg_out_of_resources
dd VERR_ALREADY_EXISTS, msg_already_exists
dd VERR_INTERNAL_ERROR, msg_internal_error
dd VERR_INTERRUPTED, msg_interrupted
dd VERR_RESOURCE_BUSY, msg_resource_busy
; Ошибки HGCM
dd VERR_HGCM_SERVICE_NOT_FOUND, msg_hgcm_service_not_found
dd VERR_HGCM_CLIENT_REJECTED, msg_hgcm_client_rejected
dd VERR_HGCM_INVALID_CMD_ADDRESS, msg_hgcm_invalid_cmd
dd VERR_HGCM_INTERNAL, msg_hgcm_internal
dd VERR_HGCM_INVALID_CLIENT_ID, msg_hgcm_invalid_client
dd VERR_HGCM_PROTOCOL_ERROR, msg_hgcm_protocol_error
dd VERR_HGCM_TOO_MANY_CLIENTS, msg_hgcm_too_many_clients
dd VERR_HGCM_TOO_MANY_PARMS, msg_hgcm_too_many_parms
; Ошибки Clipboard
dd VERR_SHCLPB_NO_DATA, msg_shclpb_no_data
dd VERR_SHCLPB_FORMAT_NOT_SUPPORTED, msg_shclpb_format_not_supported
error_table_end:
error_table_count equ (error_table_end - error_table) / 8
; =============================================================================
; ПРОЦЕДУРА: error_get_message
; Назначение: Получение человеко-читаемого сообщения по коду ошибки
; Вход : EAX = код ошибки
; Выход: EAX = указатель на строку сообщения
; =============================================================================
proc error_get_message uses ebx ecx edi
mov ebx, eax ; Сохраняем код ошибки
mov edi, error_table ; Указатель на таблицу
mov ecx, error_table_count ; Количество записей
.loop:
cmp dword [edi], ebx ; Сравниваем код
je .found
add edi, 8 ; Следующая запись
loop .loop
; Ошибка не найдена
mov eax, msg_unknown_error
ret
.found:
mov eax, [edi + 4] ; Возвращаем указатель на сообщение
ret
endp
; =============================================================================
; ПРОЦЕДУРА: error_is_success
; Назначение: Проверка, является ли код успешным
; Вход : EAX = код
; Выход: EAX = 0 если успех, != 0 если ошибка
; ZF = 1 если успех
; =============================================================================
proc error_is_success
test eax, eax
jns .success ; Если >= 0, это успех
; Это ошибка
or eax, 1
ret
.success:
xor eax, eax
ret
endp
; =============================================================================
; ПРОЦЕДУРА: error_is_hgcm
; Назначение: Проверка, является ли ошибка HGCM-специфичной
; Вход : EAX = код ошибки
; Выход: EAX = 0 если HGCM ошибка, != 0 в противном случае
; ZF = 1 если HGCM ошибка
; =============================================================================
proc error_is_hgcm
cmp eax, -2910
jl .not_hgcm
cmp eax, -2900
jg .not_hgcm
xor eax, eax ; Это HGCM ошибка
ret
.not_hgcm:
or eax, 1
ret
endp
; =============================================================================
; МАКРОСЫ ДЛЯ ПРОВЕРКИ ОШИБОК
; ============================================================================= ; =============================================================================
; Проверка кода возврата VMMDev и переход при ошибке ; Success codes (positive or zero)
macro CHECK_VMMDEV_RC label_error { VINF_SUCCESS equ 0
stdcall error_is_success VINF_HGCM_ASYNC_EXECUTE equ 2903 ; Запрос выполняется асинхронно
jnz label_error VBOX_HGCM_REQ_DONE equ 0x00000001 ; Флаг завершения в поле flags
} HGCM_TIMEOUT_DEFAULT equ 500000 ; Базовый таймаут
; Проверка результата HGCM и переход при ошибке ; err.h
macro CHECK_HGCM_RESULT ptr, label_error { VINF_NOT_SUPPORTED equ 37
mov eax, [ptr + HGCM_HEADER.result] VERR_ACCESS_DENIED equ -38
stdcall error_is_success VERR_INTERRUPTED equ -39
jnz label_error VINF_INTERRUPTED equ 39
}
; Проверка EAX на ошибку (отрицательное значение) ; =============================================================================
macro CHECK_ERROR label_error { ; Error Codes
test eax, eax ; =============================================================================
js label_error VERR_TIMEOUT equ -78
} VERR_NOT_READY equ -25
; Проверка успеха и переход при ошибке VERR_GENERAL_FAILURE equ -1
macro CHECK_SUCCESS label_error { VERR_INVALID_PARAMETER equ -2
call error_is_success VERR_INVALID_MAGIC equ -3
jnz label_error 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
macro PRINT_ERROR { ; VERR_WRONG_PARAMETER_TYPE-22416
push eax
call error_get_message VERR_NO_DATA equ -125 ; Нэт доступных данных
invoke SysMsgBoardStr, eax
pop eax
} ; =============================================================================
; 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
; Комбинированная проверка и вывод
macro CHECK_AND_PRINT label_error {
test eax, eax
jns @f
push eax
call error_get_message
invoke SysMsgBoardStr, eax
pop eax
jmp label_error
@@:
}

View File

@@ -1,174 +0,0 @@
; =============================================================================
; VBoxGuest Driver for KolibriOS - Multi-level Logging System
; Supports DEBUG_LEVEL: 1 (production), 2 (development)
; =============================================================================
; Log levels:
; 1 = CRITICAL - Errors, warnings (production)
; 2 = INFO - Detailed operation logs (development)
; =============================================================================
; Core Logging Macros
; =============================================================================
; ; Log at CRITICAL level (always shown if __DEBUG__ = 1)
; macro LOG_CRITICAL msg, [args] {
; common
; if __DEBUG__ eq 1
; DEBUGF 1, "[vbox] " # msg, args
; end if
; }
; Log at INFO level (shown only if __DEBUG_LEVEL__ >= 2)
macro LOG_INFO msg, [args] {
common
if __DEBUG__ eq 1
if __DEBUG_LEVEL__ >= 2
DEBUGF 2, "[vbox] " # msg, args
end if
end if
}
; =============================================================================
; Subsystem-specific Logging
; =============================================================================
; ; VMMDev protocol logs
; macro LOG_VMMDEV level, msg, [args] {
; common
; if __DEBUG__ eq 1
; if level <= __DEBUG_LEVEL__
; DEBUGF level, "[vmmdev] " # msg, args
; end if
; end if
; }
; ; HGCM protocol logs
; macro LOG_HGCM level, msg, [args] {
; common
; if __DEBUG__ eq 1
; if level <= __DEBUG_LEVEL__
; DEBUGF level, "[hgcm] " # msg, args
; end if
; end if
; }
; ; Clipboard service logs
; macro LOG_CLIP level, msg, [args] {
; common
; if __DEBUG__ eq 1
; if level <= __DEBUG_LEVEL__
; DEBUGF level, "[clip] " # msg, args
; end if
; end if
; }
; ; Display service logs
; macro LOG_DISP level, msg, [args] {
; common
; if __DEBUG__ eq 1
; if level <= __DEBUG_LEVEL__
; DEBUGF level, "[display] " # msg, args
; end if
; end if
; }
; ; IRQ handler logs
; macro LOG_IRQ level, msg, [args] {
; common
; if __DEBUG__ eq 1
; if level <= __DEBUG_LEVEL__
; DEBUGF level, "[irq] " # msg, args
; end if
; end if
; }
; =============================================================================
; Error Logging with Return Code
; =============================================================================
; Log error with return code (always at level 1)
macro LOG_ERROR subsystem, operation, rc {
; if __DEBUG__ eq 1
push eax esi
mov eax, rc
stdcall error_get_message, eax
mov esi, eax
DEBUGF 1, "[" # subsystem # "] " # operation # " failed: %s (rc=0x%x)\n", esi, rc
pop esi eax
; end if
}
; Log VMMDev error
macro LOG_VMMDEV_ERROR operation, rc {
LOG_ERROR "vmmdev", operation, rc
}
; Log HGCM error
macro LOG_HGCM_ERROR operation, rc {
LOG_ERROR "hgcm", operation, rc
}
; ; Trace function exit
; macro TRACE_EXIT func_name {
; if __DEBUG__ eq 1
; ; if __DEBUG_LEVEL__ >= 2
; DEBUGF 2, "[trace] <<< %s\n", func_name
; ; end if
; end if
; }
; ; Trace function with return value
; macro TRACE_EXIT_RC func_name, rc {
; ; if __DEBUG__ eq 1
; ; if __DEBUG_LEVEL__ >= 2
; DEBUGF 2, "[trace] <<< %s -> 0x%x\n", func_name, rc
; ; end if
; ; end if
; }
macro LOG_HGCM_PACKET addr {
if __DEBUG__ eq 1
if __DEBUG_LEVEL__ >= 2
push eax ebx ecx edx
mov eax, [addr + HGCM_HEADER.header.size]
mov ebx, [addr + HGCM_HEADER.header.request_type]
mov ecx, [addr + HGCM_HEADER.header.rc]
DEBUGF 2, "[hgcm] Packet: size=%d, type=0x%x, rc=0x%x\n", eax, ebx, ecx
pop edx ecx ebx eax
end if
end if
}
; =============================================================================
; Data Dump (DEBUG_LEVEL 2)
; =============================================================================
; Dump memory region
macro LOG_DUMP_MEMORY addr, size {
push eax ebx ecx esi
mov esi, addr
mov ecx, size
DEBUGF 2, "[dump] Memory at 0x%x, size=%d bytes:\n", esi, ecx
cmp ecx, 64
jbe .dump_all
mov ecx, 64
.dump_all:
test ecx, ecx
jz .dump_done
xor ebx, ebx
.dump_loop:
mov al, [esi + ebx]
DEBUGF 2, "0x%x ", al
inc ebx
test ebx, 0x0F
jnz .no_newline
DEBUGF 2, "\n"
.no_newline:
cmp ebx, ecx
jb .dump_loop
DEBUGF 2, "\n"
.dump_done:
pop esi ecx ebx eax
}

View File

@@ -1,108 +0,0 @@
; =============================================================================
; VBoxGuest Driver for KolibriOS - Helper Macros
; VBox request sending, VGA control, HGCM packet initialization
; =============================================================================
; =============================================================================
; VirtualBox Request Sending
; =============================================================================
macro vbox_send_pack {
movzx edx, word [vbox_device.port]
DEBUGF 2, "Sending request: port=0x%x, phys_addr=0x%x\n", edx, eax
mov dx, [vbox_device.port]
out dx, eax
; Small delay for processing
push ecx
mov ecx, 1000
.wait:
in al, 0x80 ; I/O wait
loop .wait
pop ecx
}
; =============================================================================
; VGA Control Macros
; =============================================================================
macro vga_write_reg index_val, data_val {
mov eax, index_val
mov dx, VGA_INDEX_PORT
out dx, ax
mov eax, data_val
mov dx, VGA_DATA_PORT
out dx, ax
}
macro vga_set_mode w, h, bpp {
vga_write_reg VGA_INDEX_ENABLE, VGA_DISABLED
vga_write_reg VGA_INDEX_XRES, w
vga_write_reg VGA_INDEX_YRES, h
vga_write_reg VGA_INDEX_BPP, bpp
vga_write_reg VGA_INDEX_ENABLE, VGA_ENABLED or VGA_LFB_ENABLED
}
; =============================================================================
; HGCM Packet Initialization Macros
; =============================================================================
macro hgcm_init_header ptr, pkt_size, req_type {
mov dword [ptr + HGCM_HEADER.header.size], pkt_size
mov dword [ptr + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [ptr + HGCM_HEADER.header.request_type], req_type
mov dword [ptr + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
mov dword [ptr + HGCM_HEADER.header.reserved1], 0
mov dword [ptr + HGCM_HEADER.header.reserved2], 0
mov dword [ptr + HGCM_HEADER.flags], 0
mov dword [ptr + HGCM_HEADER.result], VERR_INTERNAL_ERROR
}
macro hgcm_set_parm_u32 ptr, val {
mov dword [ptr + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
mov dword [ptr + HGCM_PARM.value_or_size], val
mov dword [ptr + HGCM_PARM.offset_or_addr], 0
}
macro hgcm_set_parm_ptr ptr, psize, paddr {
mov dword [ptr + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_IN
mov dword [ptr + HGCM_PARM.value_or_size], psize
mov dword [ptr + HGCM_PARM.offset_or_addr], paddr
}
; =============================================================================
; Error Checking Macros
; =============================================================================
macro CHECK_VMMDEV_RC label_error {
stdcall error_is_success
jnz label_error
}
macro CHECK_HGCM_RESULT ptr, label_error {
mov eax, [ptr + HGCM_HEADER.result]
stdcall error_is_success
jnz label_error
}
macro hgcm_set_parm_ptr_out dest, size, addr
{
mov dword [dest + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_OUT
mov dword [dest + HGCM_PARM.value_or_size], size
mov dword [dest + HGCM_PARM.offset_or_addr], addr
}
macro hgcm_set_parm32_u32 ptr, val {
mov dword [ptr + 0], 1 ; type = 32BIT
mov dword [ptr + 4], val ; value32
mov dword [ptr + 8], 0 ; unused
}
macro hgcm_set_parm32_linaddr_out ptr, size, addr {
mov dword [ptr + 0], 9 ; type = LINADDR_KERNEL_OUT
mov dword [ptr + 4], size ; buffer size
mov dword [ptr + 8], addr ; linear address
}

View File

@@ -1,357 +0,0 @@
; =============================================================================
; VBoxGuest Driver for KolibriOS - Data Structures
; =============================================================================
; =============================================================================
; VMMDev Structures
; =============================================================================
struct VMMDEV_HEADER
size dd ?
version dd ?
request_type dd ?
rc dd ?
reserved1 dd ?
reserved2 dd ?
ends
struct VMMDEV_GUEST_INFO
header VMMDEV_HEADER
version dd ?
os_type dd ?
ends
struct VMMDEV_GUEST_CAPS
header VMMDEV_HEADER
caps dd ?
ends
struct VMMDEV_ACK_EVENTS
header VMMDEV_HEADER
events dd ?
ends
struct VMMDEV_DISPLAY_CHANGE
header VMMDEV_HEADER
x_res dd ?
y_res dd ?
bpp dd ?
event_ack dd ?
ends
; =============================================================================
; VMMDev MMIO структура для KolibriOS
; =============================================================================
struct VMMDEV_MEMORY
version dd ? ; 0x00 версия структуры
revision dd ? ; 0x04 ревизия драйвера
reserved1 dd ? ; 0x08 зарезервировано
request_type dd ? ; 0x0C тип запроса
request_result dd ? ; 0x10 результат запроса
event_flags dd ? ; 0x14 pending события от хоста (читаем)
event_ack dd ? ; 0x18 подтверждение событий (пишем)
mouse_status dd ? ; 0x1C статус мыши
mouse_data dd ? ; 0x20 данные мыши
mouse_data_last dd ? ; 0x24 последние данные мыши
absolute_mouse dd ? ; 0x28 абсолютные координаты мыши
event_enabled dd ? ; 0x2C включённые события (R/W)
padding rb 0xD0 ; 0x100 - 0x30 = 0xD0
ends
; Константы событий
VMMDEV_EVENT_MOUSE_POSITION_CHANGED equ 0x00000001
VMMDEV_EVENT_MOUSE_CAPTURED equ 0x00000002
VMMDEV_EVENT_MOUSE_GUEST_NEEDS_CAPTURE equ 0x00000004
VMMDEV_EVENT_MOUSE_GUEST_CAN_CAPTURE equ 0x00000008
VMMDEV_EVENT_MOUSE_GUEST_RELEASED equ 0x00000010
VMMDEV_EVENT_DISPLAY_CHANGE equ 0x00000020
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE equ 0x00000040
VMMDEV_EVENT_GRAPHICS_CHANGE equ 0x00000080
VMMDEV_EVENT_HGCM equ 0x00000100
VMMDEV_EVENT_BALLOON_CHANGE equ 0x00000200
VMMDEV_EVENT_STATISTICS_INTERVAL equ 0x00000400
VMMDEV_EVENT_VBVA_ENABLED equ 0x00000800
VMMDEV_EVENT_HGCM_DRV_UNLOAD equ 0x00001000
; =============================================================================
; HGCM Structures
; =============================================================================
struct HGCM_HEADER
header VMMDEV_HEADER
flags dd ?
result dd ?
ends
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
client_id dd ?
function dd ?
param_count dd ?
ends
struct HGCM_PARM
type dd ?
value_or_size dd ?
offset_or_addr dd ?
ends
; =============================================================================
; Clipboard Structures
; =============================================================================
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
; =============================================================================
; Driver Internal Structures
; =============================================================================
struct VBOX_DEVICE
port dw ?
pad1 dw ?
mmio dd ?
mmio_phys dd ?
ack_virt dd ?
ack_phys dd ?
display_virt dd ?
display_phys dd ?
hgcm_timeout dd ?
hgcm_connect_virt dd ?
hgcm_connect_phys dd ?
hgcm_disconnect_virt dd ?
hgcm_disconnect_phys dd ?
hgcm_call_virt dd ?
hgcm_call_phys dd ?
ends
struct CLIPBOARD_STATE
client_id dd ?
connected dd ?
state dd ? ; FSM: 0=INIT, 1=CONNECTING, 2=READY
; pending dd ? ; Флаг pending event для worker
formats_host dd ?
formats_guest dd ?
buffer_virt dd ? ; Основной буфер (UTF-8/raw)
buffer_phys dd ?
buffer_size dd ?
utf16_buffer dd ? ; Временный буфер для UTF-16LE конвертации
utf16_size dd ?
last_data_size dd ?
ends
struct DISPLAY
x dd ?
y dd ?
width dd ?
height dd ?
bits_per_pixel dd ?
vrefresh dd ?
pitch dd ?
lfb dd ?
ends
; =============================================================================
; VirtualBox SharedFolder Structures (ALIGNED)
; =============================================================================
struct SF_STATE
connected dd ?
client_id dd ?
folder_count dd ?
pad1 dd ?
folders rb sizeof.SF_FOLDER * SHFL_MAX_MAPPINGS
ends
struct SF_FOLDER
active dd ?
root_handle dd ?
disk_handle dd ?
disk_number dd ?
name rb 256
host_path rb 1024
ends
; =============================================================================
; HGCM packets for SharedFolder operations (ALIGNED)
; =============================================================================
struct SHFL_QUERY_MAPPINGS
header HGCM_CALL
flags HGCM_PARM
count HGCM_PARM
buffer HGCM_PARM
ends
struct SHFL_QUERY_MAP_NAME
header HGCM_CALL
index HGCM_PARM
name HGCM_PARM
ends
struct SHFL_MAP_FOLDER
header HGCM_CALL
path HGCM_PARM
root HGCM_PARM
delimiter HGCM_PARM
case_sens HGCM_PARM
ends
struct SHFL_UNMAP_FOLDER
header HGCM_CALL
root HGCM_PARM
ends
struct SHFL_CREATE
header HGCM_CALL
root HGCM_PARM
path HGCM_PARM
parms HGCM_PARM
ends
struct SHFLCREATEPARMS
handle dd ?
result dd ?
flags dd ?
pad1 dd ?
info rb 96
ends
struct SHFL_CLOSE
header HGCM_CALL
root HGCM_PARM
handle HGCM_PARM
ends
struct SHFL_READ
header HGCM_CALL
root HGCM_PARM
handle HGCM_PARM
offset HGCM_PARM
size HGCM_PARM
buffer HGCM_PARM
ends
struct SHFL_WRITE
header HGCM_CALL
root HGCM_PARM
handle HGCM_PARM
offset HGCM_PARM
size HGCM_PARM
buffer HGCM_PARM
ends
struct SHFL_LIST
header HGCM_CALL
root HGCM_PARM
path HGCM_PARM
flags HGCM_PARM
buffer_size HGCM_PARM
buffer HGCM_PARM
resume_pt HGCM_PARM
file_count HGCM_PARM
ends
struct SHFL_INFORMATION
header HGCM_CALL
root HGCM_PARM
handle HGCM_PARM
flags HGCM_PARM
info HGCM_PARM
ends
struct SHFL_REMOVE
header HGCM_CALL
root HGCM_PARM
path HGCM_PARM
flags HGCM_PARM
ends
struct SHFL_RENAME
header HGCM_CALL
root HGCM_PARM
src HGCM_PARM
dst HGCM_PARM
flags HGCM_PARM
ends
struct SHFL_FLUSH
header HGCM_CALL
root HGCM_PARM
handle HGCM_PARM
ends
; =============================================================================
; Helper structures (ALIGNED)
; =============================================================================
struct SHFLSTRING
size dw ?
length dw ?
string rb 4092
ends
struct SHFLFSOBJINFO
size dq ?
allocated dq ?
access_time dq ?
modification_time dq ?
change_time dq ?
birth_time dq ?
attr dd ?
pad1 dd ?
reserved rb 40
ends
struct SHFLMAPPING
flags dd ?
root dd ?
reserved rb 40
ends
; struct SHFLMAPPING
; flags dd ?
; root dd ?
; ends

133
common/utils.inc Normal file
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

41
config.inc Normal file
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 ; Логирование диспетчера

13
core/core.inc Normal file
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

@@ -1,290 +0,0 @@
; =============================================================================
; HGCM Protocol - ИСПРАВЛЕННАЯ ВЕРСИЯ
; Исправления:
; 1. Правильная инициализация HGCM_CONNECT (очистка буфера имени)
; 2. Увеличенный таймаут для async операций
; 3. Улучшенная обработка ошибок
; 4. Детальное логирование для отладки
; =============================================================================
proc hgcm_wait_async uses ebx ecx esi, request_ptr:dword
mov esi, [request_ptr]
; ИСПРАВЛЕНИЕ #1: Увеличен таймаут в 10 раз для первого подключения
; Первое HGCM подключение может занять больше времени
mov dword [vbox_device.hgcm_timeout], HGCM_TIMEOUT_DEFAULT * 1000
DEBUGF 2, "[hgcm] Async wait started, timeout=%d cycles\n", [vbox_device.hgcm_timeout]
.wait_loop:
; CPU Memory Barrier - обязателен перед чтением DMA памяти
; Гарантирует что CPU видит последние изменения от устройства
mfence
; Читаем флаги из пакета
mov eax, [esi + HGCM_HEADER.flags]
DEBUGF 3, "[hgcm] Polling flags: 0x%x\n", eax
; Проверяем флаг VBOX_HGCM_REQ_DONE (0x1)
; Этот флаг устанавливается хостом когда запрос выполнен
test eax, VBOX_HGCM_REQ_DONE
jnz .completed
; Уменьшаем счетчик таймаута
dec dword [vbox_device.hgcm_timeout]
jz .timeout
; Короткая задержка между проверками
; PAUSE инструкция оптимизирует spin-loop на x86
mov ecx, 10000
.delay_loop:
pause
loop .delay_loop
jmp .wait_loop
.timeout:
; КРИТИЧЕСКАЯ ОШИБКА: хост не ответил
DEBUGF 1, "[hgcm] *** ASYNC TIMEOUT ***\n"
DEBUGF 1, "[hgcm] Current flags: 0x%x\n", [esi + HGCM_HEADER.flags]
DEBUGF 1, "[hgcm] Request RC: 0x%x\n", [esi + HGCM_HEADER.header.rc]
LOG_DUMP_MEMORY esi, 64
mov eax, VERR_TIMEOUT
ret
.completed:
; Финальный barrier перед чтением результата
mfence
DEBUGF 2, "[hgcm] Async completed! flags=0x%x, rc=0x%x, result=0x%x\n", \
[esi + HGCM_HEADER.flags], \
[esi + HGCM_HEADER.header.rc], \
[esi + HGCM_HEADER.result]
xor eax, eax
ret
endp
proc hgcm_send_request uses ebx edx esi edi, request_ptr:dword
DEBUGF 2, ">>> hgcm_send_request\n"
mov esi, [request_ptr]
; Получаем физический адрес пакета
mov eax, esi
invoke GetPhysAddr
mov edx, eax
; Проверка что физический адрес валиден
test eax, eax
jz .phys_addr_failed
DEBUGF 2, "[hgcm] Request: virt=0x%x, phys=0x%x, size=%d\n", \
esi, edx, [esi + HGCM_HEADER.header.size]
LOG_DUMP_MEMORY esi, 48
; Отправляем запрос через VMMDev I/O порт
stdcall vmmdev_request_perform, edx
; Первая проверка: транспортный RC
mov eax, [esi + HGCM_HEADER.header.rc]
DEBUGF 2, "[hgcm] Transport RC: 0x%x\n", eax
; Проверяем асинхронное выполнение
cmp eax, VINF_HGCM_ASYNC_EXECUTE
je .async_request
; Синхронный запрос - проверяем ошибки транспорта
test eax, eax
js .transport_error
jmp .check_service_result
.async_request:
DEBUGF 2, "[hgcm] Async execution, waiting for completion...\n"
; Ожидаем завершения async операции
stdcall hgcm_wait_async, esi
test eax, eax
jnz .async_wait_failed
; Проверяем финальный транспортный RC
mov eax, [esi + HGCM_HEADER.header.rc]
test eax, eax
js .transport_error_after_async
; Продолжаем проверку результата сервиса
jmp .check_service_result
.async_wait_failed:
DEBUGF 1, "[hgcm] Async wait failed: 0x%x\n", eax
ret
.check_service_result:
; Вторая проверка: результат HGCM сервиса
mov eax, [esi + HGCM_HEADER.result]
DEBUGF 2, "[hgcm] Service result: 0x%x\n", eax
test eax, eax
js .service_error
; Успех!
DEBUGF 2, "[hgcm] Request SUCCESS\n"
DEBUGF 2, "<<< hgcm_send_request -> 0\n"
xor eax, eax
ret
.phys_addr_failed:
DEBUGF 1, "[hgcm] ERROR: GetPhysAddr failed!\n"
mov eax, VERR_INVALID_POINTER
ret
.transport_error:
push eax
stdcall error_get_message, eax
mov ebx, eax
pop eax
DEBUGF 1, "[hgcm] VMMDev transport error: %s (rc=0x%x)\n", ebx, eax
ret
.transport_error_after_async:
push eax
stdcall error_get_message, eax
mov ebx, eax
pop eax
DEBUGF 1, "[hgcm] Transport error after async: %s (rc=0x%x)\n", ebx, eax
ret
.service_error:
push eax
stdcall error_get_message, eax
mov ebx, eax
pop eax
DEBUGF 1, "[hgcm] HGCM service error: %s (result=0x%x)\n", ebx, eax
ret
endp
proc hgcm_connect uses ebx ecx edx esi edi, service_name:dword
DEBUGF 2, ">>> hgcm_connect\n"
mov edi, hgcm_connect_pkt
; =========================================================================
; ИСПРАВЛЕНИЕ #2: Правильная инициализация HGCM_CONNECT структуры
; =========================================================================
; 1. Инициализируем VMMDEV заголовок (24 байта)
mov dword [edi + HGCM_HEADER.header.size], sizeof.HGCM_CONNECT
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CONNECT
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
mov dword [edi + HGCM_HEADER.header.reserved1], 0
mov dword [edi + HGCM_HEADER.header.reserved2], 0
; 2. Инициализируем HGCM поля (8 байт)
mov dword [edi + HGCM_HEADER.flags], 0
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
; 3. Устанавливаем тип локации
; HGCM_LOC_TYPE_PREDEFINED = 2 (встроенный сервис VBox)
mov dword [edi + HGCM_CONNECT.location_type], HGCM_LOC_TYPE_PREDEFINED
; 4. КРИТИЧНО: Очищаем весь буфер service_name
; Это исправляет проблему с мусором в неиспользованных байтах
push edi
lea edi, [edi + HGCM_CONNECT.service_name]
xor eax, eax
mov ecx, HGCM_SERVICE_NAME_MAX / 4 ; 128/4 = 32 dword
rep stosd
pop edi
; 5. Копируем имя сервиса
mov esi, [service_name]
push edi
lea edi, [edi + HGCM_CONNECT.service_name]
mov ecx, HGCM_SERVICE_NAME_MAX - 1 ; Оставляем место для \0
.copy_name:
lodsb
test al, al
jz .name_done
stosb
dec ecx
jnz .copy_name
.name_done:
; Гарантируем null-termination
xor al, al
stosb
pop edi
; 6. Инициализируем client_id = 0 (OUT параметр, будет заполнен хостом)
mov dword [edi + HGCM_CONNECT.client_id], 0
; Логируем пакет перед отправкой
push edi
lea edi, [edi + HGCM_CONNECT.service_name]
DEBUGF 2, "[hgcm] Connecting to service: '%s'\n", edi
pop edi
DEBUGF 2, "[hgcm] Packet size: %d bytes\n", sizeof.HGCM_CONNECT
LOG_DUMP_MEMORY edi, sizeof.HGCM_CONNECT
; 7. Отправляем запрос
stdcall hgcm_send_request, edi
test eax, eax
jnz .error
; 8. Проверяем что получили client_id != 0
mov eax, [edi + HGCM_CONNECT.client_id]
test eax, eax
jz .no_client_id
DEBUGF 2, "[hgcm] *** CONNECTED *** Client ID: 0x%x\n", eax
DEBUGF 2, "<<< hgcm_connect -> 0x%x\n", eax
ret
.no_client_id:
DEBUGF 1, "[hgcm] ERROR: client_id = 0 (хост не вернул ID)\n"
DEBUGF 1, "[hgcm] Возможные причины:\n"
DEBUGF 1, "[hgcm] 1. Сервис не запущен на хосте\n"
DEBUGF 1, "[hgcm] 2. Неправильное имя сервиса\n"
DEBUGF 1, "[hgcm] 3. VBox Guest Additions отключены\n"
xor eax, eax
ret
.error:
DEBUGF 1, "[hgcm] Connect failed with error: 0x%x\n", eax
DEBUGF 2, "<<< hgcm_connect -> 0\n"
xor eax, eax
ret
endp
proc hgcm_disconnect uses edi, client_id:dword
DEBUGF 2, ">>> hgcm_disconnect\n"
mov edi, hgcm_disconnect_pkt
; Инициализируем заголовок
mov dword [edi + HGCM_HEADER.header.size], sizeof.HGCM_DISCONNECT
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_DISCONNECT
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
mov dword [edi + HGCM_HEADER.header.reserved1], 0
mov dword [edi + HGCM_HEADER.header.reserved2], 0
mov dword [edi + HGCM_HEADER.flags], 0
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
; Устанавливаем client_id
mov eax, [client_id]
mov [edi + HGCM_DISCONNECT.client_id], eax
DEBUGF 2, "[hgcm] Disconnecting client: 0x%x\n", eax
; Отправляем запрос
stdcall hgcm_send_request, edi
DEBUGF 2, "<<< hgcm_disconnect\n"
ret
endp
; Глобальные буферы для HGCM пакетов
align 4
hgcm_connect_pkt: rb sizeof.HGCM_CONNECT
hgcm_disconnect_pkt: rb sizeof.HGCM_DISCONNECT

View File

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

74
core/mmio.inc Normal file
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

117
core/pci.inc Normal file
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

54
core/ports.inc Normal file
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

70
core/state.inc Normal file
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

57
core/timer.inc Normal file
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

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

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

135
data/core/constants.inc Normal file
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

297
data/core/structs.inc Normal file
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

26
data/data.inc Normal file
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 )

17
data/display/structs.inc Normal file
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

107
data/hgcm/constants.inc Normal file
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
}

70
data/hgcm/structs.inc Normal file
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

40
data/mouse/constants.inc Normal file
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 )

43
data/mouse/structs.inc Normal file
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 )

29
data/seamless/structs.inc Normal file
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 )

12
data/timesync/structs.inc Normal file
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

63
hgcm/async.inc Normal file
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

395
hgcm/call.inc Normal file
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

5
hgcm/core.inc Normal file
View File

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

276
hgcm/hgcm.inc Normal file
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

78
hgcm/params.inc Normal file
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

@@ -1,929 +0,0 @@
; =============================================================================
; Clipboard Service - ИСПРАВЛЕННАЯ ВЕРСИЯ
;
; Ключевые изменения по сравнению с оригиналом:
; 1. FSM (Finite State Machine): INIT CONNECTING READY
; 2. UTF-8 UTF-16LE конвертация (как в Linux драйвере)
; 3. Правильная обработка VERR_RESOURCE_BUSY
; 4. Проверки готовности данных перед чтением
; 5. НЕТ HGCM вызовов до state == READY
; =============================================================================
align 4
clipboard_state CLIPBOARD_STATE
clipboard_service_name db 'VBoxSharedClipboard', 0
; =============================================================================
; ПРОЦЕДУРА: clipboard_init
; Инициализация clipboard с FSM
; =============================================================================
proc clipboard_init uses ebx ecx edx esi edi
DEBUGF 1, "=================================================\n"
DEBUGF 1, "[clip] >>> clipboard_init STARTED\n"
DEBUGF 1, "=================================================\n"
; Инициализация FSM state
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
mov ax, [vbox_device.port]
test ax, ax
jz .vmmdev_not_ready
mov eax, [vbox_device.mmio]
test eax, eax
jz .vmmdev_not_ready
DEBUGF 2, "[clip] VMMDev ready: port=0x%x, mmio=0x%x\n", \
[vbox_device.port], [vbox_device.mmio]
cmp dword [clipboard_state.connected], 1
je .already_connected
DEBUGF 2, "[clip] Not connected yet, initializing...\n"
DEBUGF 2, "[clip] Step 1/6: Setting state to CONNECTING...\n"
; =========================================================================
; FSM: INIT CONNECTING
; =========================================================================
mov dword [clipboard_state.state], CLIPBOARD_STATE_CONNECTING
DEBUGF 2, "[clip] Step 2/6: Connecting to HGCM service...\n"
stdcall hgcm_connect, clipboard_service_name
test eax, eax
jz .connect_failed
mov [clipboard_state.client_id], eax
mov dword [clipboard_state.connected], 1
DEBUGF 1, "[clip] *** HGCM CONNECTED *** client_id=0x%x\n", eax
DEBUGF 2, "[clip] Step 3/6: Allocating main buffer (%d bytes)...\n", \
VBOX_SHCL_MAX_CHUNK_SIZE
invoke KernelAlloc, VBOX_SHCL_MAX_CHUNK_SIZE
test eax, eax
jz .alloc_failed
mov [clipboard_state.buffer_virt], eax
mov dword [clipboard_state.buffer_size], VBOX_SHCL_MAX_CHUNK_SIZE
mov dword [clipboard_state.last_data_size], 0
DEBUGF 2, "[clip] Main buffer allocated: virt=0x%x, size=%d\n", \
eax, VBOX_SHCL_MAX_CHUNK_SIZE
; =========================================================================
; Выделяем UTF-16 буфер (в 2 раза больше для worst case)
; =========================================================================
DEBUGF 2, "[clip] Step 4/6: Allocating UTF-16 buffer...\n"
invoke KernelAlloc, VBOX_SHCL_MAX_CHUNK_SIZE * 2
test eax, eax
jz .alloc_utf16_failed
mov [clipboard_state.utf16_buffer], eax
mov dword [clipboard_state.utf16_size], VBOX_SHCL_MAX_CHUNK_SIZE * 2
DEBUGF 2, "[clip] UTF-16 buffer allocated: virt=0x%x, size=%d\n", \
eax, [clipboard_state.utf16_size]
DEBUGF 2, "[clip] Step 5/6: Getting physical address...\n"
mov eax, [clipboard_state.buffer_virt]
invoke GetPhysAddr
test eax, eax
jz .phys_failed
mov [clipboard_state.buffer_phys], eax
DEBUGF 2, "[clip] Buffer phys=0x%x\n", eax
DEBUGF 2, "[clip] Step 6/6: Initializing HGCM packets...\n"
call clipboard_init_packets
DEBUGF 2, "[clip] Packets initialized\n"
; =========================================================================
; Сообщаем что у нас НЕТ данных при инициализации
; =========================================================================
DEBUGF 2, "[clip] Reporting initial formats (none)...\n"
stdcall clipboard_report_formats, 0
test eax, eax
jnz .report_failed
; =========================================================================
; FSM: CONNECTING READY
; С ЭТОГО МОМЕНТА IRQ может обрабатывать clipboard события
; =========================================================================
mov dword [clipboard_state.state], CLIPBOARD_STATE_READY
DEBUGF 1, "=================================================\n"
DEBUGF 1, "[clip] *** CLIPBOARD READY ***\n"
DEBUGF 1, "[clip] FSM state : READY\n"
DEBUGF 1, "[clip] client_id : 0x%x\n", [clipboard_state.client_id]
DEBUGF 1, "[clip] buffer_virt : 0x%x\n", [clipboard_state.buffer_virt]
DEBUGF 1, "[clip] buffer_phys : 0x%x\n", [clipboard_state.buffer_phys]
DEBUGF 1, "[clip] buffer_size : %d bytes\n", [clipboard_state.buffer_size]
DEBUGF 1, "[clip] utf16_buffer : 0x%x\n", [clipboard_state.utf16_buffer]
DEBUGF 1, "[clip] utf16_size : %d bytes\n", [clipboard_state.utf16_size]
DEBUGF 1, "=================================================\n"
xor eax, eax
ret
.vmmdev_not_ready:
DEBUGF 1, "[clip] ERROR: VMMDev not initialized!\n"
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
mov eax, VERR_GENERAL_FAILURE
ret
.already_connected:
DEBUGF 2, "[clip] Already connected (client_id=0x%x, state=%d)\n", \
[clipboard_state.client_id], [clipboard_state.state]
xor eax, eax
ret
.connect_failed:
DEBUGF 1, "[clip] *** CRITICAL: HGCM connect failed ***\n"
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
mov eax, VERR_HGCM_SERVICE_NOT_FOUND
ret
.alloc_failed:
DEBUGF 1, "[clip] ERROR: Main buffer alloc failed\n"
stdcall hgcm_disconnect, [clipboard_state.client_id]
mov dword [clipboard_state.connected], 0
mov dword [clipboard_state.client_id], 0
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
mov eax, VERR_NO_MEMORY
ret
.alloc_utf16_failed:
DEBUGF 1, "[clip] ERROR: UTF-16 buffer alloc failed\n"
invoke KernelFree, [clipboard_state.buffer_virt]
stdcall hgcm_disconnect, [clipboard_state.client_id]
mov dword [clipboard_state.connected], 0
mov dword [clipboard_state.client_id], 0
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
mov eax, VERR_NO_MEMORY
ret
.phys_failed:
DEBUGF 1, "[clip] ERROR: GetPhysAddr failed\n"
invoke KernelFree, [clipboard_state.utf16_buffer]
invoke KernelFree, [clipboard_state.buffer_virt]
stdcall hgcm_disconnect, [clipboard_state.client_id]
mov dword [clipboard_state.connected], 0
mov dword [clipboard_state.client_id], 0
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
mov eax, VERR_GENERAL_FAILURE
ret
.report_failed:
DEBUGF 1, "[clip] WARNING: report_formats failed (non-critical)\n"
; НЕ сбрасываем state - это не критично
xor eax, eax
ret
endp
; =============================================================================
; ПРОЦЕДУРА: clipboard_init_packets
; Без изменений - как в оригинале
; =============================================================================
proc clipboard_init_packets uses eax ebx ecx edi
DEBUGF 2, "[clip] >>> clipboard_init_packets\n"
mov eax, [clipboard_state.client_id]
test eax, eax
jz .error_no_client
; MSG_GET packet
mov edi, shcl_msg_get
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_MSG_GET
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
mov dword [edi + HGCM_HEADER.header.reserved1], 0
mov dword [edi + HGCM_HEADER.header.reserved2], 0
mov dword [edi + HGCM_HEADER.flags], 0
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
mov eax, [clipboard_state.client_id]
mov [edi + SHCL_MSG_GET.header.client_id], eax
mov dword [edi + SHCL_MSG_GET.header.function], VBOX_SHCL_GUEST_FN_MSG_GET
mov dword [edi + SHCL_MSG_GET.header.param_count], 2
hgcm_set_parm_u32 edi + SHCL_MSG_GET.msg_type, 0
hgcm_set_parm_u32 edi + SHCL_MSG_GET.formats, 0
; FORMATS_REPORT packet
mov edi, shcl_formats_report
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_FORMATS_REPORT
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
mov dword [edi + HGCM_HEADER.header.reserved1], 0
mov dword [edi + HGCM_HEADER.header.reserved2], 0
mov dword [edi + HGCM_HEADER.flags], 0
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
mov eax, [clipboard_state.client_id]
mov [edi + SHCL_FORMATS_REPORT.header.client_id], eax
mov dword [edi + SHCL_FORMATS_REPORT.header.function], VBOX_SHCL_GUEST_FN_FORMATS_REPORT
mov dword [edi + SHCL_FORMATS_REPORT.header.param_count], 1
hgcm_set_parm_u32 edi + SHCL_FORMATS_REPORT.formats, 0
; DATA_READ packet
mov edi, shcl_data_read
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_DATA_READ
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
mov dword [edi + HGCM_HEADER.header.reserved1], 0
mov dword [edi + HGCM_HEADER.header.reserved2], 0
mov dword [edi + HGCM_HEADER.flags], 0
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
mov eax, [clipboard_state.client_id]
mov [edi + SHCL_DATA_READ.header.client_id], eax
mov dword [edi + SHCL_DATA_READ.header.function], VBOX_SHCL_GUEST_FN_DATA_READ
mov dword [edi + SHCL_DATA_READ.header.param_count], 3
hgcm_set_parm_u32 edi + SHCL_DATA_READ.format, 0
hgcm_set_parm_ptr edi + SHCL_DATA_READ.buffer, 0, 0
hgcm_set_parm_u32 edi + SHCL_DATA_READ.size, 0
; DATA_WRITE packet
mov edi, shcl_data_write
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_DATA_WRITE
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
mov dword [edi + HGCM_HEADER.header.reserved1], 0
mov dword [edi + HGCM_HEADER.header.reserved2], 0
mov dword [edi + HGCM_HEADER.flags], 0
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
mov eax, [clipboard_state.client_id]
mov [edi + SHCL_DATA_WRITE.header.client_id], eax
mov dword [edi + SHCL_DATA_WRITE.header.function], VBOX_SHCL_GUEST_FN_DATA_WRITE
mov dword [edi + SHCL_DATA_WRITE.header.param_count], 2
hgcm_set_parm_u32 edi + SHCL_DATA_WRITE.format, 0
hgcm_set_parm_ptr edi + SHCL_DATA_WRITE.buffer, 0, 0
xor eax, eax
ret
.error_no_client:
DEBUGF 1, "[clip] ERROR: No client_id for packet init\n"
; mov eax, VERR_INVALID_STATE
ret
endp
; =============================================================================
; ПРОЦЕДУРА: clipboard_get_host_msg
; ИСПРАВЛЕНО: Правильная обработка VERR_RESOURCE_BUSY
; =============================================================================
proc clipboard_get_host_msg uses ebx esi edi
DEBUGF 2, "[clip] >>> clipboard_get_host_msg\n"
; Проверка FSM state
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
jne .not_ready
mov esi, shcl_msg_get
test esi, esi
jz .error
; Reset output parameters
lea edi, [esi + SHCL_MSG_GET.msg_type]
hgcm_set_parm_u32 edi, 0
lea edi, [esi + SHCL_MSG_GET.formats]
hgcm_set_parm_u32 edi, 0
mov eax, [clipboard_state.buffer_phys]
stdcall hgcm_send_request, esi, eax
test eax, eax
jnz .hgcm_error
; Получаем результаты
mov eax, [esi + HGCM_HEADER.result]
test eax, eax
jnz .check_interrupted
mov eax, [esi + SHCL_MSG_GET.msg_type + HGCM_PARM.value_or_size]
mov ebx, [esi + SHCL_MSG_GET.formats + HGCM_PARM.value_or_size]
DEBUGF 2, "[clip] Got message: type=%d, formats=0x%x\n", eax, ebx
ret
.check_interrupted:
; =========================================================================
; ИСПРАВЛЕНО: Правильная обработка VERR_RESOURCE_BUSY (0xFFFFFF76 -138)
; Это означает что сервис сбросился или изменил состояние
; =========================================================================
cmp eax, 0xFFFFFF76 ; VERR_RESOURCE_BUSY
jne .check_invalid_param
DEBUGF 1, "[clip] VERR_RESOURCE_BUSY - service state changed\n"
; Сброс форматов при INTERRUPTED
mov dword [clipboard_state.formats_host], 0
mov dword [clipboard_state.formats_guest], 0
xor eax, eax ; Возвращаем "нет сообщения"
xor ebx, ebx
ret
.check_invalid_param:
cmp eax, 0xFFFFFFE7 ; VERR_INVALID_PARAMETER
jne .check_other_errors
DEBUGF 1, "[clip] VERR_INVALID_PARAMETER\n"
jmp .error
.check_other_errors:
; Другие ошибки
DEBUGF 1, "[clip] GET_HOST_MSG error: 0x%x\n", eax
jmp .error
.not_ready:
DEBUGF 1, "[clip] ERROR: Clipboard not ready (state=%d)\n", \
[clipboard_state.state]
; mov eax, VERR_INVALID_STATE
ret
.hgcm_error:
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
ret
.error:
mov eax, VERR_GENERAL_FAILURE
ret
endp
; =============================================================================
; Clipboard Service - ИСПРАВЛЕННАЯ ВЕРСИЯ (ЧАСТЬ 2)
; Функции чтения/записи с UTF-8 UTF-16LE конвертацией
; =============================================================================
; =============================================================================
; ПРОЦЕДУРА: clipboard_read_data
; ИСПРАВЛЕНО: Добавлена конвертация UTF-16LE UTF-8
; =============================================================================
proc clipboard_read_data uses ebx esi edi, format:dword, buffer:dword, buffer_size:dword
DEBUGF 1, "[clip] >>> clipboard_read_data(format=0x%x, buf=0x%x, size=%d)\n", \
[format], [buffer], [buffer_size]
; Проверка FSM state
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
jne .not_ready
mov eax, [buffer]
test eax, eax
jz .invalid_param
mov ecx, [buffer_size]
test ecx, ecx
jz .invalid_param
; Проверяем формат
mov eax, [format]
test eax, eax
jz .invalid_param
; =========================================================================
; Читаем в UTF-16 буфер сначала (VirtualBox отдает UTF-16LE)
; =========================================================================
mov esi, shcl_data_read
test esi, esi
jz .error
lea edi, [esi + SHCL_DATA_READ.format]
mov eax, [format]
hgcm_set_parm_u32 edi, eax
lea edi, [esi + SHCL_DATA_READ.buffer]
mov eax, [clipboard_state.utf16_buffer] ; Читаем в UTF-16 буфер!
mov ebx, [clipboard_state.buffer_phys]
hgcm_set_parm_ptr edi, eax, ebx
lea edi, [esi + SHCL_DATA_READ.size]
mov eax, [clipboard_state.utf16_size]
hgcm_set_parm_u32 edi, eax
DEBUGF 2, "[clip] Reading to UTF-16 buffer: virt=0x%x, phys=0x%x, max=%d\n", \
[clipboard_state.utf16_buffer], [clipboard_state.buffer_phys], \
[clipboard_state.utf16_size]
mov eax, [clipboard_state.buffer_phys]
stdcall hgcm_send_request, esi, eax
test eax, eax
jnz .hgcm_error
; Проверяем результат
mov eax, [esi + HGCM_HEADER.result]
test eax, eax
jnz .read_failed
; Получаем реальный размер прочитанных данных (UTF-16LE)
mov eax, [esi + SHCL_DATA_READ.size + HGCM_PARM.value_or_size]
test eax, eax
jz .no_data
DEBUGF 2, "[clip] Read %d bytes (UTF-16LE with BOM)\n", eax
; Сохраняем размер UTF-16 данных
push eax ; Размер UTF-16
; =========================================================================
; Конвертируем UTF-16LE UTF-8
; =========================================================================
DEBUGF 2, "[clip] Converting UTF-16LE → UTF-8...\n"
stdcall utf16le_to_utf8, \
[clipboard_state.utf16_buffer], \ ; src (UTF-16LE)
eax, \ ; src_len
[buffer], \ ; dst (UTF-8)
[buffer_size] ; dst_max
pop ebx ; Восстанавливаем UTF-16 size
cmp eax, -1
je .conversion_failed
; EAX = размер UTF-8 данных (без null)
DEBUGF 1, "[clip] Conversion OK: %d bytes UTF-16LE → %d bytes UTF-8\n", ebx, eax
; Сохраняем размер для последующего использования
mov [clipboard_state.last_data_size], eax
ret
.no_data:
DEBUGF 2, "[clip] No data read (size=0)\n"
xor eax, eax
ret
.read_failed:
DEBUGF 1, "[clip] READ_DATA failed: rc=0x%x\n", eax
mov eax, -1
ret
.conversion_failed:
DEBUGF 1, "[clip] ERROR: UTF-16LE → UTF-8 conversion failed\n"
mov eax, -1
ret
.not_ready:
DEBUGF 1, "[clip] ERROR: Clipboard not ready\n"
mov eax, -1
ret
.invalid_param:
DEBUGF 1, "[clip] ERROR: Invalid parameters\n"
mov eax, -1
ret
.hgcm_error:
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
mov eax, -1
ret
.error:
DEBUGF 1, "[clip] ERROR: Internal error\n"
mov eax, -1
ret
endp
; =============================================================================
; ПРОЦЕДУРА: clipboard_write_data
; ИСПРАВЛЕНО: Добавлена конвертация UTF-8 UTF-16LE
; =============================================================================
proc clipboard_write_data uses ebx esi edi, format:dword, buffer:dword, size:dword
DEBUGF 1, "[clip] >>> clipboard_write_data(format=0x%x, buf=0x%x, size=%d)\n", \
[format], [buffer], [size]
; Проверка FSM state
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
jne .not_ready
mov eax, [buffer]
test eax, eax
jz .invalid_param
mov ecx, [size]
test ecx, ecx
jz .invalid_param
; =========================================================================
; Конвертируем UTF-8 UTF-16LE перед отправкой
; =========================================================================
DEBUGF 2, "[clip] Converting UTF-8 → UTF-16LE...\n"
; stdcall utf8_to_utf16le, \
; [buffer], \ ; src (UTF-8)
; [size], \ ; src_len
; [clipboard_state.utf16_buffer], \ ; dst (UTF-16LE)
; [clipboard_state.utf16_size] ; dst_max
cmp eax, -1
je .conversion_failed
; EAX = размер UTF-16LE данных (с BOM)
push eax ; Сохраняем для отчета
DEBUGF 2, "[clip] Conversion OK: %d bytes UTF-8 → %d bytes UTF-16LE (with BOM)\n", \
[size], eax
; =========================================================================
; Отправляем UTF-16LE данные в VirtualBox
; =========================================================================
mov esi, shcl_data_write
test esi, esi
jz .error_cleanup
lea edi, [esi + SHCL_DATA_WRITE.format]
mov eax, [format]
hgcm_set_parm_u32 edi, eax
lea edi, [esi + SHCL_DATA_WRITE.buffer]
mov eax, [clipboard_state.utf16_buffer] ; Отправляем из UTF-16 буфера!
mov ebx, [clipboard_state.buffer_phys]
pop ecx ; Размер UTF-16
hgcm_set_parm_ptr edi, eax, ecx
DEBUGF 2, "[clip] Writing UTF-16LE: virt=0x%x, phys=0x%x, size=%d\n", \
eax, ebx, ecx
mov eax, [clipboard_state.buffer_phys]
stdcall hgcm_send_request, esi, eax
test eax, eax
jnz .hgcm_error
; Проверяем результат
mov eax, [esi + HGCM_HEADER.result]
test eax, eax
jnz .write_failed
DEBUGF 1, "[clip] Write successful\n"
xor eax, eax
ret
.write_failed:
DEBUGF 1, "[clip] WRITE_DATA failed: rc=0x%x\n", eax
ret
.conversion_failed:
DEBUGF 1, "[clip] ERROR: UTF-8 → UTF-16LE conversion failed\n"
mov eax, VERR_GENERAL_FAILURE
ret
.error_cleanup:
pop eax ; Cleanup stack
.not_ready:
DEBUGF 1, "[clip] ERROR: Clipboard not ready\n"
; mov eax, VERR_INVALID_STATE
ret
.invalid_param:
DEBUGF 1, "[clip] ERROR: Invalid parameters\n"
mov eax, VERR_INVALID_PARAMETER
ret
.hgcm_error:
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
ret
.error:
DEBUGF 1, "[clip] ERROR: Internal error\n"
mov eax, VERR_GENERAL_FAILURE
ret
endp
; =============================================================================
; ПРОЦЕДУРА: clipboard_report_formats
; Без изменений - как в оригинале
; =============================================================================
proc clipboard_report_formats uses ebx esi edi, formats:dword
DEBUGF 2, "[clip] >>> clipboard_report_formats(0x%x)\n", [formats]
; Проверка FSM state
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
jne .not_ready_skip_check
; При инициализации может быть вызвано до READY - пропускаем проверку
.not_ready_skip_check:
mov esi, shcl_formats_report
test esi, esi
jz .error
lea edi, [esi + SHCL_FORMATS_REPORT.formats]
mov eax, [formats]
hgcm_set_parm_u32 edi, eax
; Сохраняем наши форматы
mov eax, [formats]
mov [clipboard_state.formats_guest], eax
DEBUGF 2, "[clip] Reporting formats: 0x%x\n", eax
mov eax, [clipboard_state.buffer_phys]
stdcall hgcm_send_request, esi, eax
test eax, eax
jnz .hgcm_error
mov eax, [esi + HGCM_HEADER.result]
test eax, eax
jnz .report_failed
DEBUGF 2, "[clip] Formats reported successfully\n"
xor eax, eax
ret
.report_failed:
DEBUGF 1, "[clip] REPORT_FORMATS failed: rc=0x%x\n", eax
ret
.hgcm_error:
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
ret
.error:
DEBUGF 1, "[clip] ERROR: Internal error\n"
mov eax, VERR_GENERAL_FAILURE
ret
endp
; =============================================================================
; ПРОЦЕДУРА: clipboard_handle_event
; ИСПРАВЛЕНО: Вызывается ТОЛЬКО когда state == READY
; =============================================================================
proc clipboard_handle_event uses ebx esi edi
DEBUGF 2, "[clip] >>> clipboard_handle_event\n"
; Double-check FSM state (должно быть проверено в IRQ, но для надежности)
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
jne .not_ready
cmp dword [clipboard_state.connected], 1
jne .not_connected
; Получаем сообщение от хоста
stdcall clipboard_get_host_msg
cmp eax, -1
je .error
; EAX = msg_type, EBX = formats
test eax, eax
jz .done ; Нет сообщения
DEBUGF 1, "[clip] Host message: type=%d, formats=0x%x\n", eax, ebx
; Сохраняем форматы хоста
mov [clipboard_state.formats_host], ebx
; Обрабатываем по типу сообщения
cmp eax, 0 ; VBOX_SHCL_HOST_MSG_FORMATS_REPORT
je .host_formats_changed
cmp eax, 1 ; VBOX_SHCL_HOST_MSG_READ_DATA
je .host_wants_read
cmp eax, 2 ; VBOX_SHCL_HOST_MSG_READ_DATA_CID
je .host_wants_read
cmp eax, 3 ; VBOX_SHCL_HOST_MSG_REPORT_FORMATS
je .host_formats_changed
DEBUGF 1, "[clip] Unknown message type: %d\n", eax
jmp .done
.host_formats_changed:
DEBUGF 1, "[clip] Host clipboard formats changed: 0x%x\n", ebx
; Проверяем есть ли текст
test ebx, VBOX_SHCL_FMT_UNICODETEXT
jz .no_text_format
DEBUGF 1, "[clip] Host has text, ready to read on demand\n"
jmp .done
.no_text_format:
DEBUGF 2, "[clip] Host clipboard has no text\n"
jmp .done
.host_wants_read:
DEBUGF 1, "[clip] Host wants to read our clipboard data\n"
; Проверяем есть ли у нас данные
mov eax, [clipboard_state.formats_guest]
test eax, eax
jz .check_last_data
; Проверяем текстовый формат
test eax, VBOX_SHCL_FMT_UNICODETEXT
jz .no_text_to_send
; Проверяем буфер
mov eax, [clipboard_state.last_data_size]
test eax, eax
jz .send_empty
; Отправляем данные (уже в UTF-8, будет конвертировано в write_data)
DEBUGF 1, "[clip] Sending %d bytes UTF-8 to host\n", eax
mov edi, [clipboard_state.buffer_virt]
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, edi, eax
jmp .done
.check_last_data:
mov eax, [clipboard_state.last_data_size]
test eax, eax
jz .send_empty
DEBUGF 1, "[clip] Sending last read data (%d bytes)\n", eax
mov edi, [clipboard_state.buffer_virt]
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, edi, eax
stdcall clipboard_report_formats, VBOX_SHCL_FMT_UNICODETEXT
jmp .done
.send_empty:
DEBUGF 1, "[clip] Sending empty string\n"
mov edi, [clipboard_state.buffer_virt]
mov byte [edi], 0
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, edi, 1
jmp .done
.no_text_to_send:
DEBUGF 1, "[clip] Host wants text but we don't have it\n"
jmp .done
.done:
DEBUGF 2, "[clip] <<< clipboard_handle_event\n"
ret
.not_ready:
DEBUGF 1, "[clip] ERROR: Clipboard not ready (state=%d)\n", \
[clipboard_state.state]
ret
.error:
DEBUGF 1, "[clip] ERROR: Failed to get host message\n"
ret
.not_connected:
DEBUGF 2, "[clip] <<< clipboard_handle_event (not connected)\n"
ret
endp
; =============================================================================
; HIGH-LEVEL API с кодировкой
; =============================================================================
proc clipboard_set_text uses ebx esi edi, text_ptr:dword
DEBUGF 1, "[clip] >>> clipboard_set_text(0x%x)\n", [text_ptr]
mov esi, [text_ptr]
test esi, esi
jz .invalid_param
; Определяем длину UTF-8 строки
xor ecx, ecx
mov edi, esi
.strlen_loop:
lodsb
test al, al
jz .strlen_done
inc ecx
cmp ecx, VBOX_SHCL_MAX_CHUNK_SIZE - 4
jae .strlen_done
jmp .strlen_loop
.strlen_done:
test ecx, ecx
jz .invalid_param
mov esi, [text_ptr]
; inc ecx ; НЕ включаем null - конвертер добавит
DEBUGF 2, "[clip] Text length: %d bytes UTF-8\n", ecx
; clipboard_write_data сконвертирует UTF-8 UTF-16LE
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, esi, ecx
test eax, eax
jnz .write_failed
; Сохраняем в локальный буфер для последующей отправки хосту
mov esi, [text_ptr]
mov edi, [clipboard_state.buffer_virt]
rep movsb
mov dword [clipboard_state.formats_guest], VBOX_SHCL_FMT_UNICODETEXT
mov [clipboard_state.last_data_size], ecx
xor eax, eax
ret
.write_failed:
DEBUGF 1, "[clip] ERROR: Failed to write text\n"
ret
.invalid_param:
DEBUGF 1, "[clip] ERROR: Invalid text\n"
mov eax, VERR_INVALID_PARAMETER
ret
endp
proc clipboard_get_text uses ebx esi edi, buffer:dword, buffer_size:dword
DEBUGF 1, "[clip] >>> clipboard_get_text(buffer=0x%x, size=%d)\n", \
[buffer], [buffer_size]
mov edi, [buffer]
test edi, edi
jz .invalid_param
mov eax, [buffer_size]
test eax, eax
jz .invalid_param
; Проверяем формат хоста
mov eax, [clipboard_state.formats_host]
test eax, VBOX_SHCL_FMT_UNICODETEXT
jz .no_text
; clipboard_read_data сконвертирует UTF-16LE UTF-8
stdcall clipboard_read_data, VBOX_SHCL_FMT_UNICODETEXT, [buffer], [buffer_size]
cmp eax, -1
je .error
DEBUGF 1, "[clip] Got text: %d bytes UTF-8\n", eax
ret
.no_text:
DEBUGF 2, "[clip] No text in host clipboard\n"
xor eax, eax
ret
.invalid_param:
DEBUGF 1, "[clip] ERROR: Invalid parameters\n"
mov eax, -1
ret
.error:
DEBUGF 1, "[clip] ERROR: Failed to read text\n"
mov eax, -1
ret
endp
; =============================================================================
; Shutdown
; =============================================================================
proc clipboard_shutdown uses eax esi
DEBUGF 2, "[clip] >>> clipboard_shutdown\n"
; FSM: INIT
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
cmp dword [clipboard_state.connected], 1
jne .not_connected
stdcall hgcm_disconnect, [clipboard_state.client_id]
; Free UTF-16 buffer
mov eax, [clipboard_state.utf16_buffer]
test eax, eax
jz .no_utf16_buffer
invoke KernelFree, eax
.no_utf16_buffer:
; Free main buffer
mov eax, [clipboard_state.buffer_virt]
test eax, eax
jz .no_buffer
invoke KernelFree, eax
.no_buffer:
mov dword [clipboard_state.connected], 0
mov dword [clipboard_state.client_id], 0
mov dword [clipboard_state.buffer_virt], 0
mov dword [clipboard_state.buffer_phys], 0
mov dword [clipboard_state.utf16_buffer], 0
mov dword [clipboard_state.utf16_size], 0
mov dword [clipboard_state.last_data_size], 0
DEBUGF 2, "[clip] Clipboard shutdown complete\n"
.not_connected:
ret
endp
; =============================================================================
; DATA
; =============================================================================
align 4
shcl_msg_get: rb sizeof.SHCL_MSG_GET
shcl_formats_report: rb sizeof.SHCL_FORMATS_REPORT
shcl_data_read: rb sizeof.SHCL_DATA_READ
shcl_data_write: rb sizeof.SHCL_DATA_WRITE

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

@@ -1,78 +0,0 @@
; =============================================================================
; Display Service - Auto-resize on window change
; =============================================================================
align 4
kos_display_ptr dd ?
proc display_change uses ebx ecx edx esi edi
DEBUGF 2, "display_change\n"
; Request display change information
mov eax, [vbox_device.display_phys]
stdcall vmmdev_request_perform, eax
mov ebx, [vbox_device.display_virt]
mov edi, [ebx + VMMDEV_DISPLAY_CHANGE.x_res]
mov esi, [ebx + VMMDEV_DISPLAY_CHANGE.y_res]
mov ecx, [ebx + VMMDEV_DISPLAY_CHANGE.bpp]
DEBUGF 2, "[display] Requested resolution: %dx%d %dbpp\n", edi, esi, ecx
; Validate minimum
cmp edi, DISP_W_MIN
jb .skip
cmp esi, DISP_H_MIN
jb .skip
; Validate maximum
cmp edi, DISP_W_MAX
ja .skip
cmp esi, DISP_H_MAX
ja .skip
; Validate BPP
cmp ecx, 32
je .valid
cmp ecx, 24
je .valid
cmp ecx, 16
je .valid
cmp ecx, 8
jne .skip
.valid:
DEBUGF 2, "[display] Setting resolution %dx%d %dbpp\n", edi, esi, ecx
; Set video mode using VGA
cli
vga_set_mode edi, esi, ecx
sti
; Update KolibriOS display structure
mov eax, [kos_display_ptr]
mov [eax + DISPLAY.width], edi
mov [eax + DISPLAY.height], esi
mov [eax + DISPLAY.bits_per_pixel], ecx
; Calculate pitch
push ebx
mov ebx, ecx
shr ecx, 3
imul ecx, edi
mov [eax + DISPLAY.pitch], ecx
pop ebx
; Apply changes
mov eax, edi
mov edx, esi
dec eax
dec edx
invoke SetScreen
DEBUGF 2, "[display] Resolution set successfully\n"
.skip:
DEBUGF 2, "[trace] <<< display_change\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

382
services/mouse/mouse.inc Normal file
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

11
services/services.inc Normal file
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'

View File

@@ -1,341 +0,0 @@
; =============================================================================
; SharedFolder Service - FIXED VERSION
; =============================================================================
VBOX_SHAREDFOLDER_SERVICE db 'VBoxSharedFolders', 0
align 4
sf_state SF_STATE
; =============================================================================
; ПРОЦЕДУРА: sf_init
; =============================================================================
proc sf_init uses ebx ecx edx esi edi
DEBUGF 1, "=================================================\n"
DEBUGF 1, "[SF] >>> SharedFolder initialization STARTED\n"
DEBUGF 1, "=================================================\n"
mov ax, [vbox_device.port]
test ax, ax
jz .vmmdev_not_ready
mov eax, [vbox_device.mmio]
test eax, eax
jz .vmmdev_not_ready
DEBUGF 1, "[SF] VMMDev ready: port=0x%x, mmio=0x%x\n", \
[vbox_device.port], [vbox_device.mmio]
DEBUGF 1, "[SF] Connecting to SharedFolders HGCM service...\n"
stdcall hgcm_connect, VBOX_SHAREDFOLDER_SERVICE
test eax, eax
jz .connect_failed
mov [sf_state.client_id], eax
mov dword [sf_state.connected], 1
DEBUGF 1, "[SF] *** HGCM CONNECTED *** client_id=0x%x\n", eax
DEBUGF 1, "[SF] Querying available shared folders...\n"
call sf_query_mappings
DEBUGF 1, "[SF] After sf_query_mappings, eax=0x%x\n", eax
test eax, eax
jnz .query_failed
DEBUGF 1, "=================================================\n"
DEBUGF 1, "[SF] *** SHAREDFOLDER INITIALIZED SUCCESSFULLY ***\n"
DEBUGF 1, "[SF] client_id : 0x%x\n", [sf_state.client_id]
DEBUGF 1, "[SF] folder_count : %d\n", [sf_state.folder_count]
DEBUGF 1, "=================================================\n"
xor eax, eax
ret
.vmmdev_not_ready:
DEBUGF 1, "[SF] ERROR: VMMDev not initialized!\n"
mov eax, -1
ret
.connect_failed:
DEBUGF 1, "[SF] *** CRITICAL: HGCM connect failed ***\n"
mov eax, -1
ret
.query_failed:
DEBUGF 1, "[SF] WARNING: Query mappings failed (non-fatal)\n"
xor eax, eax
ret
endp
; =============================================================================
; ПРОЦЕДУРА: sf_query_mappings
; ПОЛНОСТЬЮ ИСПРАВЛЕННАЯ ВЕРСИЯ
; =============================================================================
proc sf_query_mappings uses ebx esi edi
DEBUGF 1, "[SF] ========================================\n"
DEBUGF 1, "[SF] >>> sf_query_mappings STARTED\n"
DEBUGF 1, "[SF] ========================================\n"
; =========================================================================
; ШАГ 1: Получить физические адреса
; =========================================================================
DEBUGF 1, "[SF] STEP 1: Buffer preparation\n"
; Получить физический адрес буфера
mov eax, sf_mappings_buffer
invoke GetPhysAddr
mov ebx, eax ; ebx = buffer_phys
DEBUGF 1, "[SF] buffer_virt = 0x%x\n", sf_mappings_buffer
DEBUGF 1, "[SF] buffer_phys = 0x%x\n", ebx
; Вычислить PFN (Page Frame Number)
mov eax, ebx
shr eax, PAGE_SHIFT
mov [sf_pagelist_pfn], eax
DEBUGF 1, "[SF] buffer_pfn = 0x%x\n", eax
; Получить физический адрес pagelist структуры
mov eax, sf_pagelist
invoke GetPhysAddr
mov esi, eax ; esi = pagelist_phys
DEBUGF 1, "[SF] pagelist_phys = 0x%x\n", esi
; =========================================================================
; ШАГ 2: Формирование HGCM пакета (56 байт как в Clipboard!)
; =========================================================================
DEBUGF 1, "[SF] STEP 2: Building HGCM packet (56 bytes)\n"
; Используем пакет на 56 байт (как в работающем Clipboard запросе)
mov edi, sf_query_mappings_pkt
; Очистка пакета (56 байт)
push edi
mov ecx, 56/4
xor eax, eax
rep stosd
pop edi
; =========================================================================
; ВАЖНО: Сравниваем с работающим Clipboard запросом!
; Clipboard пакет (56 байт):
; 0x38 0x00 0x00 0x00 0x01 0x00 0x01 0x00 0x3E 0x00 0x00 0x00 0x1F 0xFF 0xFF 0xFF
; 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x1F 0xFF 0xFF 0xFF
; 0x03 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x01 0x00 0x00 0x00
; =========================================================================
; Заполняем пакет ТОЧНО как в Clipboard запросе, но с нашими параметрами
; VMMDev Request Header (первые 24 байта)
mov dword [edi + 0], 56 ; size = 56 байт (0x38) как в Clipboard!
mov dword [edi + 4], VMMDEV_REQUEST_HEADER_VERSION
mov dword [edi + 8], VMMDEV_HGCM_CALL32
; КРИТИЧНО: rc должен быть 0x1FFFFFFF (как в работающем запросе)!
mov dword [edi + 12], 0x1FFFFFFF ; rc = VERR_HGCM_ASYNC_EXECUTE
; mov dword [edi + 12], 0
mov dword [edi + 16], 0 ; reserved1
mov dword [edi + 20], 0 ; reserved2
; HGCM Call Header (смещение 24)
mov eax, [sf_state.client_id]
mov dword [edi + 24], eax ; clientID
mov dword [edi + 28], SHFL_FN_QUERY_MAPPINGS ; function
mov dword [edi + 32], 3 ; cParms = 3 параметра
; КРИТИЧНО: Еще одно поле rc в HGCM header (смещение 36)!
mov dword [edi + 36], 0x1FFFFFFF ; Еще один rc (как в Clipboard)
; =========================================================================
; Параметры (начинаются с 40 байта)
; В 56-байтовом пакете у нас есть место для 3 параметров по 4+4 байта каждый
; =========================================================================
; Параметр 0: flags (U32, IN) - смещение 40
mov dword [edi + 40], HGCM_PARM_TYPE_32BIT ; type = 1
mov dword [edi + 44], 0 ; flags = 0
; Параметр 1: count (U32, OUT) - смещение 44+4=48
mov dword [edi + 48], HGCM_PARM_TYPE_32BIT ; type = 1
mov dword [edi + 52], 0 ; начальное значение = 0
; Параметр 2: buffer (PAGELIST, OUT) - смещение 48+8=56
; Но у нас пакет всего 56 байт! Значит, параметры идут последовательно.
; Исправляем: параметр 2 начинается с 52+4=56, но это уже за пределами 56 байт!
; ВНИМАНИЕ: В пакете Clipboard параметры идут последовательно без пропусков:
; offset 40: param0.type
; offset 44: param0.value
; offset 48: param1.type
; offset 52: param1.value
; offset 56: param2.type - но это уже за пределами 56 байт!
; Значит, пакет должен быть больше 56 байт!
; Попробуем 80 байт, как было раньше, но с правильными смещениями
; =========================================================================
; ВЕРСИЯ 2: Пакет на 80 байт с правильными смещениями
; =========================================================================
DEBUGF 1, "[SF] STEP 2a: Rebuilding packet (80 bytes with correct offsets)\n"
; Переопределяем пакет на 80 байт
mov dword [edi + 0], 80 ; size = 80 байт
; Очищаем оставшуюся часть пакета
push edi
add edi, 56
mov ecx, (80-56)/4
xor eax, eax
rep stosd
pop edi
; Теперь параметр 2 должен начинаться с 56 байта
mov dword [edi + 56], HGCM_PARM_TYPE_PAGELIST ; type = 8
mov dword [edi + 60], 1 ; cPages = 1
mov dword [edi + 64], esi ; paPages = физический адрес pagelist
; Padding для 80-байтового пакета
mov dword [edi + 68], 0
mov dword [edi + 72], 0
mov dword [edi + 76], 0
DEBUGF 1, "[SF] Packet structure (80 bytes):\n"
DEBUGF 1, "[SF] size: 80 (0x50)\n"
DEBUGF 1, "[SF] rc: 0x%x\n", [edi + 12]
DEBUGF 1, "[SF] clientID: 0x%x\n", [sf_state.client_id]
DEBUGF 1, "[SF] function: %d\n", SHFL_FN_QUERY_MAPPINGS
DEBUGF 1, "[SF] cParms: ?\n"
DEBUGF 1, "[SF] param0: type=1, value=0\n"
DEBUGF 1, "[SF] param1: type=1, value=0\n"
DEBUGF 1, "[SF] param2: type=8, cPages=?, paPages=0x%x\n", esi
; Дамп пакета для проверки
LOG_DUMP_MEMORY edi, 80
; =========================================================================
; ШАГ 3: Отправка запроса
; =========================================================================
DEBUGF 1, "[SF] STEP 3: Sending HGCM request...\n"
stdcall hgcm_send_request, edi
DEBUGF 1, "[SF] hgcm_send_request returned: 0x%x\n", eax
test eax, eax
jnz .request_failed
; =========================================================================
; ШАГ 4: Анализ результата
; =========================================================================
DEBUGF 1, "[SF] STEP 4: Analyzing response\n"
; Проверка кода возврата VMMDev
mov eax, [edi + 12]
DEBUGF 1, "[SF] VMMDev RC = 0x%x\n", eax
; Проверка результата HGCM
mov eax, [edi + 36] ; HGCM result code (смещение 36 в 80-байтовом пакете)
DEBUGF 1, "[SF] HGCM result = 0x%x\n", eax
; Проверяем оба кода
cmp dword [edi + 12], 0x8000B57 ; VERR_HGCM_ASYNC_EXECUTE
je .async_ok
cmp dword [edi + 12], 0
je .check_hgcm
; Ошибка VMMDev
DEBUGF 1, "[SF] VMMDev error: 0x%x\n", [edi + 12]
mov eax, -1
ret
.check_hgcm:
cmp dword [edi + 36], 0
je .success
; Ошибка HGCM
DEBUGF 1, "[SF] HGCM error: 0x%x\n", [edi + 36]
mov eax, -1
ret
.async_ok:
; Асинхронное выполнение - проверяем HGCM результат
cmp dword [edi + 36], 0
je .success
DEBUGF 1, "[SF] HGCM async error: 0x%x\n", [edi + 36]
mov eax, -1
ret
.success:
; Успех - получаем количество папок из param1.value
mov eax, [edi + 52] ; param1.value (count) - смещение 52
mov [sf_state.folder_count], eax
DEBUGF 1, "[SF] ========================================\n"
DEBUGF 1, "[SF] SUCCESS: Found %d shared folder(s)\n", eax
DEBUGF 1, "[SF] ========================================\n"
xor eax, eax
ret
.request_failed:
DEBUGF 1, "[SF] ERROR: hgcm_send_request failed (rc=0x%x)\n", eax
DEBUGF 1, "[SF] Check packet structure and sizes\n"
mov eax, -1
ret
endp
; =============================================================================
; ПРОЦЕДУРА: sf_shutdown
; =============================================================================
proc sf_shutdown uses eax
DEBUGF 1, "[SF] >>> Shutting down SharedFolder\n"
cmp dword [sf_state.connected], 1
jne .not_connected
stdcall hgcm_disconnect, [sf_state.client_id]
mov dword [sf_state.connected], 0
mov dword [sf_state.client_id], 0
DEBUGF 1, "[SF] Shutdown complete\n"
.not_connected:
ret
endp
; =============================================================================
; ДАННЫЕ
; =============================================================================
; Page-aligned буфер для данных (16384 байт)
align 4096
sf_mappings_buffer: rb 16384
; PageList структура
align 16
sf_pagelist:
dd 1 ; cPages
sf_pagelist_pfn:
dd 0 ; pfn[0]
; Структуры пакетов (в постоянной памяти)
align 16
sf_query_mappings_pkt: rb 80 ; Пакет для QUERY_MAPPINGS
sf_query_map_name_pkt: rb 80 ; Пакет для QUERY_MAP_NAME
sf_map_folder_pkt: rb 80 ; Пакет для MAP_FOLDER
; SharedFolder packets (must be in permanent memory!)
align 4
; sf_query_mappings_pkt: rb sizeof.SHFL_QUERY_MAPPINGS
; sf_query_map_name_pkt: rb sizeof.SHFL_QUERY_MAP_NAME
; sf_map_folder_pkt: rb sizeof.SHFL_MAP_FOLDER
sf_create_pkt: rb sizeof.SHFL_CREATE
sf_read_pkt: rb sizeof.SHFL_READ
sf_write_pkt: rb sizeof.SHFL_WRITE
sf_close_pkt: rb sizeof.SHFL_CLOSE
sf_list_pkt: rb sizeof.SHFL_LIST

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

70
sys/init.inc Normal file
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

183
sys/ioctl.inc Normal file
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

47
sys/shutdown.inc Normal file
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

BIN
vboxctrl/vboxctrl Normal file

Binary file not shown.

1102
vboxctrl/vboxctrl.asm Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,19 @@
; ============================================================================= ; =============================================================================
; Программа : VirtualBox Guest Driver для KolibriOS ; Программа : VirtualBox Guest Driver для KolibriOS
; Модуль : Main driver entry ; Модуль : Main driver entry
; Назначение : Full-featured driver with Clipboard support and dual-level error handling ; Назначение : Full-featured driver
; Файл : vboxguest.asm ; Файл : vboxguest.asm
; Автор : lex_coder(lex_coder@mail.ru), Алексей Михайлов
; ;
; cp /usbhd0/1/vboxguest/vboxguest /sys/Drivers/vboxguest.sys ; cp /usbhd0/1/vboxguest/vboxguest /sys/Drivers/vboxguest.sys
; loaddrv vboxguest ; loaddrv vboxguest
; ;
; ============================================================================= ; =============================================================================
format PE DLL native 0.05 format PE DLL native 0.05
entry START entry START
__DEBUG__ = 1
__DEBUG_LEVEL__ = 1
API_VERSION equ 0
SRV_GETVERSION equ 0
section '.flat' code readable writable executable section '.flat' code readable writable executable
; KolibriOS includes
include '/usbhd0/1/ksrc_n/DRIVERS/proc32.inc' include '/usbhd0/1/ksrc_n/DRIVERS/proc32.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/struct.inc' include '/usbhd0/1/ksrc_n/DRIVERS/struct.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/macros.inc' include '/usbhd0/1/ksrc_n/DRIVERS/macros.inc'
@@ -28,169 +21,59 @@ include '/usbhd0/1/ksrc_n/DRIVERS/peimport.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/fdo.inc' include '/usbhd0/1/ksrc_n/DRIVERS/fdo.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/pci.inc' include '/usbhd0/1/ksrc_n/DRIVERS/pci.inc'
; VBoxGuest driver modules include 'config.inc'
include 'common/log.inc' include 'common/common.inc'
include 'common/constants.inc' include 'data/data.inc'
include 'common/structs.inc' include 'core/core.inc'
include 'common/macros.inc' include 'vmmdev/vmmdev.inc'
include 'common/errors.inc' include 'hgcm/core.inc'
include 'core/vmmdev.inc' include 'sys/init.inc'
include 'core/hgcm.inc' include 'sys/shutdown.inc'
include 'core/irq.inc' include 'sys/ioctl.inc'
include 'services/display.inc' include 'services/services.inc'
include 'services/clipboard.inc'
include 'services/shared_folder.inc' BUILD_SERVICE_TABLE
; ============================================================================= ; =============================================================================
; Driver Entry Point ; Driver Entry Point
; ============================================================================= ; =============================================================================
proc START c, state:dword, cmdline:dword proc START c, state:dword, cmdline:dword
push ebx esi edi push ebx esi edi
cmp [state], DRV_ENTRY cmp [state], DRV_ENTRY
jne .exit je .entry
cmp [state], DRV_EXIT
je .exit
pop edi esi ebx
xor eax, eax
ret
.entry: .entry:
DEBUGF 1, "[VBoxGuest] ========================================\n" DEBUGF 2, "[VBoxGuest] VBoxGuest Driver v2.0\n"
DEBUGF 1, "[VBoxGuest] VBoxGuest Driver v1.3\n"
DEBUGF 1, "[VBoxGuest] Clipboard + Display + Dual-level Errors\n"
DEBUGF 1, "[VBoxGuest] ========================================\n"
; 1. Find PCI device call sys_init ;
stdcall detect
test eax, eax
jz .fail
; 2. Initialize device (BAR, IRQ, MMIO)
stdcall vmmdev_init, ebx
test eax, eax test eax, eax
jnz .fail jnz .fail
stdcall vbox_service_init ; Регистрация сервиса (IOCTL интерфейс для приложений)
; 3. Send guest info
call vmmdev_send_guest_info
test eax, eax
jnz .fail
; 4. Set capabilities
call vmmdev_set_guest_caps
test eax, eax
jnz .fail
; 7. Enable interrupts
call vmmdev_enable_interrupts
; 5. Initialize prepared packets
call init_packets
; 6. Initialize clipboard
call clipboard_init
test eax, eax
jnz .clipboard_failed
DEBUGF 2, "[VBoxGuest] Clipboard initialized successfully\n"
jmp .clipboard_ok
.clipboard_failed:
DEBUGF 1, "[VBoxGuest] Clipboard initialization failed (non-fatal)\n"
.clipboard_ok:
call sf_init
; 8. Set initial display
call display_change
; 9. Register service
invoke RegService, service_name, service_proc invoke RegService, service_name, service_proc
DEBUGF 1, "[VBoxGuest] ========================================\n" DEBUGF 2, "[VBoxGuest] IOCTL service '%s' registered (API v%d)\n", service_name, API_VERSION
DEBUGF 1, "[VBoxGuest] Initialization complete!\n" DEBUGF 2, "[VBoxGuest] Initialization complete!\n"
DEBUGF 1, "[VBoxGuest] ========================================\n"
pop edi esi ebx pop edi esi ebx
ret ret
.fail: .fail:
DEBUGF 1, "[VBoxGuest] Initialization FAILED\n" DEBUGF 2, "[VBoxGuest] Initialization FAILED\n"
.exit: .exit:
DEBUGF 2, "[VBoxGuest] DRV_EXIT: Shutting down driver\n"
call sys_shutdown
pop edi esi ebx pop edi esi ebx
xor eax, eax xor eax, eax
ret ret
endp endp
; =============================================================================
; Service Procedure
; =============================================================================
proc service_proc stdcall, ioctl:dword
mov ebx, [ioctl]
mov eax, [ebx + IOCTL.io_code]
cmp eax, SRV_GETVERSION
jne .fail
mov eax, [ebx + IOCTL.output]
cmp [ebx + IOCTL.out_size], 4
jne .fail
mov dword [eax], API_VERSION
xor eax, eax
ret
.fail:
or eax, -1
ret
endp
; =============================================================================
; Detect VirtualBox PCI Device
; =============================================================================
proc detect stdcall
invoke GetPCIList
mov ebx, eax
.next_dev:
mov eax, [eax + PCIDEV.fd]
cmp eax, ebx
jz .not_found
mov edx, [eax + PCIDEV.vendor_device_id]
cmp edx, (VBOX_DEVICE_ID shl 16) + VBOX_VENDOR_ID
jne .next_dev
.found:
DEBUGF 2, "[VBoxGuest] VirtualBox device found\n"
mov ebx, eax
mov eax, 1
ret
.not_found:
DEBUGF 1, "[VBoxGuest] VirtualBox device NOT found\n"
xor eax, eax
ret
endp
; =============================================================================
; Initialize Prepared Packets
; =============================================================================
proc init_packets
; ACK events packet
mov eax, vmmdev_ack
mov [vbox_device.ack_virt], eax
invoke GetPhysAddr
mov [vbox_device.ack_phys], eax
; Display change packet
mov eax, vmmdev_display
mov [vbox_device.display_virt], eax
invoke GetPhysAddr
mov [vbox_device.display_phys], eax
DEBUGF 2, "[VBoxGuest] Packets initialized\n"
ret
endp
; ============================================================================= ; =============================================================================
; Data Section ; Data Section
@@ -199,7 +82,6 @@ align 4
service_name db 'VBOXGUEST', 0 service_name db 'VBOXGUEST', 0
align 4 align 4
; VBox device structure
vbox_device VBOX_DEVICE vbox_device VBOX_DEVICE
include_debug_strings include_debug_strings

27
vmmdev/capabilities.inc Normal file
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

25
vmmdev/core.inc Normal file
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

28
vmmdev/event_filter.inc Normal file
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

77
vmmdev/guest_info.inc Normal file
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

51
vmmdev/hypervisor.inc Normal file
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

169
vmmdev/packets.inc Normal file
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

12
vmmdev/vmmdev.inc Normal file
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'