#1 init в репу
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
_
|
||||
*.txt
|
||||
vboxguest.sublime-project
|
||||
vboxguest.sublime-workspace
|
||||
~*
|
||||
149
common/constants.inc
Normal file
149
common/constants.inc
Normal 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
459
common/encoding.inc
Normal 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
272
common/errors.inc
Normal 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
174
common/log.inc
Normal 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
108
common/macros.inc
Normal 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
357
common/structs.inc
Normal 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
290
core/hgcm.inc
Normal 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
457
core/irq.inc
Normal 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
293
core/vmmdev.inc
Normal 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
929
services/clipboard.inc
Normal 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
78
services/display.inc
Normal 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
341
services/shared_folder.inc
Normal 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
209
vboxguest.asm
Normal 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
|
||||
Reference in New Issue
Block a user