#1 init в репу

This commit is contained in:
Alexey Mikhailov
2026-01-06 15:43:37 +03:00
commit 0f400bc0e0
15 changed files with 4122 additions and 0 deletions

6
.gitignore vendored Normal file
View File

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

0
README.md Normal file
View File

149
common/constants.inc Normal file
View File

@@ -0,0 +1,149 @@
; 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

459
common/encoding.inc Normal file
View File

@@ -0,0 +1,459 @@
; =============================================================================
; 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

272
common/errors.inc Normal file
View File

@@ -0,0 +1,272 @@
; =============================================================================
; VBoxGuest Driver для KolibriOS
; Модуль : Коды ошибок и обработка ошибок
; Назначение : Определения кодов ошибок VirtualBox и функции их обработки
; Источник : VBox/err.h, VBoxVideoErr.h
; =============================================================================
; =============================================================================
; Коды успеха (>= 0)
; =============================================================================
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 и переход при ошибке
macro CHECK_VMMDEV_RC label_error {
stdcall error_is_success
jnz label_error
}
; Проверка результата HGCM и переход при ошибке
macro CHECK_HGCM_RESULT ptr, label_error {
mov eax, [ptr + HGCM_HEADER.result]
stdcall error_is_success
jnz label_error
}
; Проверка EAX на ошибку (отрицательное значение)
macro CHECK_ERROR label_error {
test eax, eax
js label_error
}
; Проверка успеха и переход при ошибке
macro CHECK_SUCCESS label_error {
call error_is_success
jnz label_error
}
; Вывод сообщения об ошибке
macro PRINT_ERROR {
push eax
call error_get_message
invoke SysMsgBoardStr, eax
pop eax
}
; Комбинированная проверка и вывод
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
@@:
}

174
common/log.inc Normal file
View File

@@ -0,0 +1,174 @@
; =============================================================================
; 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
}

108
common/macros.inc Normal file
View File

@@ -0,0 +1,108 @@
; =============================================================================
; 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
}

357
common/structs.inc Normal file
View File

@@ -0,0 +1,357 @@
; =============================================================================
; 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

290
core/hgcm.inc Normal file
View File

@@ -0,0 +1,290 @@
; =============================================================================
; 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

457
core/irq.inc Normal file
View File

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

293
core/vmmdev.inc Normal file
View File

@@ -0,0 +1,293 @@
; =============================================================================
; 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

929
services/clipboard.inc Normal file
View File

@@ -0,0 +1,929 @@
; =============================================================================
; 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

78
services/display.inc Normal file
View File

@@ -0,0 +1,78 @@
; =============================================================================
; 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

341
services/shared_folder.inc Normal file
View File

@@ -0,0 +1,341 @@
; =============================================================================
; 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

209
vboxguest.asm Normal file
View File

@@ -0,0 +1,209 @@
; =============================================================================
; Программа : VirtualBox Guest Driver для KolibriOS
; Модуль : Main driver entry
; Назначение : Full-featured driver with Clipboard support and dual-level error handling
; Файл : vboxguest.asm
;
; cp /usbhd0/1/vboxguest/vboxguest /sys/Drivers/vboxguest.sys
; loaddrv vboxguest
;
; =============================================================================
format PE DLL native 0.05
entry START
__DEBUG__ = 1
__DEBUG_LEVEL__ = 1
API_VERSION equ 0
SRV_GETVERSION equ 0
section '.flat' code readable writable executable
; KolibriOS includes
include '/usbhd0/1/ksrc_n/DRIVERS/proc32.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/struct.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/macros.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/peimport.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/fdo.inc'
include '/usbhd0/1/ksrc_n/DRIVERS/pci.inc'
; VBoxGuest driver modules
include 'common/log.inc'
include 'common/constants.inc'
include 'common/structs.inc'
include 'common/macros.inc'
include 'common/errors.inc'
include 'core/vmmdev.inc'
include 'core/hgcm.inc'
include 'core/irq.inc'
include 'services/display.inc'
include 'services/clipboard.inc'
include 'services/shared_folder.inc'
; =============================================================================
; Driver Entry Point
; =============================================================================
proc START c, state:dword, cmdline:dword
push ebx esi edi
cmp [state], DRV_ENTRY
jne .exit
.entry:
DEBUGF 1, "[VBoxGuest] ========================================\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
stdcall detect
test eax, eax
jz .fail
; 2. Initialize device (BAR, IRQ, MMIO)
stdcall vmmdev_init, ebx
test eax, eax
jnz .fail
stdcall vbox_service_init
; 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
DEBUGF 1, "[VBoxGuest] ========================================\n"
DEBUGF 1, "[VBoxGuest] Initialization complete!\n"
DEBUGF 1, "[VBoxGuest] ========================================\n"
pop edi esi ebx
ret
.fail:
DEBUGF 1, "[VBoxGuest] Initialization FAILED\n"
.exit:
pop edi esi ebx
xor eax, eax
ret
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
; =============================================================================
align 4
service_name db 'VBOXGUEST', 0
align 4
; VBox device structure
vbox_device VBOX_DEVICE
include_debug_strings
align 4
data fixups
end data