VBoxGuest driver and control
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
_
|
|
||||||
*.txt
|
|
||||||
vboxguest.sublime-project
|
|
||||||
vboxguest.sublime-workspace
|
|
||||||
~*
|
|
||||||
2
common/common.inc
Normal file
2
common/common.inc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
include 'errors.inc'
|
||||||
|
include 'utils.inc'
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
; PCI Device IDs
|
|
||||||
VBOX_VENDOR_ID equ 0x80EE
|
|
||||||
VBOX_DEVICE_ID equ 0xCAFE
|
|
||||||
|
|
||||||
; VMMDev Protocol
|
|
||||||
VMMDEV_REQUEST_HEADER_VERSION equ 0x10001
|
|
||||||
VMMDEV_VERSION equ 0x00010003
|
|
||||||
VMMDEV_MEM_SIZE equ 0x00400000
|
|
||||||
VMMDEV_REQ_ACKNOWLEDGE_EVENTS equ 41
|
|
||||||
VMMDEV_REQ_REPORT_GUEST_INFO equ 50
|
|
||||||
VMMDEV_REQ_GET_DISPLAY_CHANGE equ 51
|
|
||||||
VMMDEV_REQ_SET_GUEST_CAPS equ 55
|
|
||||||
|
|
||||||
|
|
||||||
; VMMDev Events
|
|
||||||
; VMMDEV_EVENT_HGCM equ 0x00000002
|
|
||||||
; VMMDEV_EVENT_DISPLAY_CHANGE equ 0x00000004
|
|
||||||
|
|
||||||
; Guest Capabilities & OS Type
|
|
||||||
VBOXOSTYPE_KOLIBRIOS equ 0x90000
|
|
||||||
|
|
||||||
; Guest Capabilities
|
|
||||||
VMMDEV_GUEST_SUPPORTS_SEAMLESS equ 0x00000001
|
|
||||||
VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING equ 0x00000002
|
|
||||||
VMMDEV_GUEST_SUPPORTS_GRAPHICS equ 0x00000004
|
|
||||||
VMMDEV_GUEST_SUPPORTS_VRDP equ 0x00000010
|
|
||||||
VMMDEV_GUEST_SUPPORTS_HGCM equ 0x00000020
|
|
||||||
VMMDEV_GUEST_SUPPORTS_ACPI equ 0x00000040
|
|
||||||
VMMDEV_GUEST_SUPPORTS_VRDP_RESIZE equ 0x00000100
|
|
||||||
VMMDEV_GUEST_SUPPORTS_DRAG_AND_DROP equ 0x00000200
|
|
||||||
VMMDEV_GUEST_SUPPORTS_CR3_MONITORING equ 0x00000400
|
|
||||||
|
|
||||||
; Full capabilities set
|
|
||||||
VBOXGUEST_OS_SUPPORTS_CAPS equ (VMMDEV_GUEST_SUPPORTS_GRAPHICS or VMMDEV_GUEST_SUPPORTS_HGCM or VMMDEV_GUEST_SUPPORTS_DRAG_AND_DROP)
|
|
||||||
|
|
||||||
; HGCM Protocol
|
|
||||||
HGCM_PARM_TYPE_INVALID equ 0
|
|
||||||
HGCM_PARM_TYPE_32BIT equ 1
|
|
||||||
HGCM_PARM_TYPE_64BIT equ 2
|
|
||||||
HGCM_PARM_TYPE_PHYSADDR equ 3 ; deprecated
|
|
||||||
HGCM_PARM_TYPE_LINADDR equ 4
|
|
||||||
HGCM_PARM_TYPE_LINADDR_IN equ 5
|
|
||||||
HGCM_PARM_TYPE_LINADDR_OUT equ 6
|
|
||||||
HGCM_PARM_TYPE_LINADDR_LOCKED equ 7 ; НЕ ИСПОЛЬЗОВАТЬ
|
|
||||||
HGCM_PARM_TYPE_LINADDR_LOCKED_IN equ 8 ; НЕ ИСПОЛЬЗОВАТЬ
|
|
||||||
HGCM_PARM_TYPE_LINADDR_LOCKED_OUT equ 9 ; НЕ ИСПОЛЬЗОВАТЬ
|
|
||||||
HGCM_PARM_TYPE_PAGELIST equ 10
|
|
||||||
|
|
||||||
|
|
||||||
VMMDEV_HGCM_CONNECT equ 60
|
|
||||||
VMMDEV_HGCM_DISCONNECT equ 61
|
|
||||||
VMMDEV_HGCM_CALL32 equ 62
|
|
||||||
|
|
||||||
VBOX_HGCM_REQ_DONE equ 0x00000001
|
|
||||||
HGCM_LOC_TYPE_PREDEFINED equ 2
|
|
||||||
HGCM_SERVICE_NAME_MAX equ 128
|
|
||||||
HGCM_TIMEOUT_DEFAULT equ 500000
|
|
||||||
PAGE_SHIFT equ 12
|
|
||||||
|
|
||||||
; Clipboard Constants
|
|
||||||
VBOX_SHCL_GUEST_FN_MSG_GET equ 1
|
|
||||||
VBOX_SHCL_GUEST_FN_FORMATS_REPORT equ 2
|
|
||||||
VBOX_SHCL_GUEST_FN_DATA_READ equ 3
|
|
||||||
VBOX_SHCL_GUEST_FN_DATA_WRITE equ 4
|
|
||||||
VBOX_SHCL_HOST_MSG_FORMATS_REPORT equ 0
|
|
||||||
VBOX_SHCL_HOST_MSG_READ_DATA equ 1
|
|
||||||
|
|
||||||
VBOX_SHCL_FMT_UNICODETEXT equ 0x0001
|
|
||||||
VBOX_SHCL_FMT_BITMAP equ 0x0002
|
|
||||||
VBOX_SHCL_FMT_HTML equ 0x0004
|
|
||||||
VBOX_SHCL_MAX_CHUNK_SIZE equ 0x10000
|
|
||||||
|
|
||||||
; Display/VGA Constants
|
|
||||||
VGA_INDEX_PORT equ 0x01CE
|
|
||||||
VGA_DATA_PORT equ 0x01CF
|
|
||||||
VGA_INDEX_ENABLE equ 0x4
|
|
||||||
VGA_INDEX_XRES equ 0x1
|
|
||||||
VGA_INDEX_YRES equ 0x2
|
|
||||||
VGA_INDEX_BPP equ 0x3
|
|
||||||
VGA_DISABLED equ 0x00
|
|
||||||
VGA_ENABLED equ 0x01
|
|
||||||
VGA_LFB_ENABLED equ 0x40
|
|
||||||
DISP_W_MIN equ 640
|
|
||||||
DISP_H_MIN equ 480
|
|
||||||
DISP_W_MAX equ 3840
|
|
||||||
DISP_H_MAX equ 2160
|
|
||||||
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; SharedFolder
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; HGCM функции для SharedFolder сервиса
|
|
||||||
SHFL_FN_QUERY_MAPPINGS equ 1 ; Получить список mappings
|
|
||||||
SHFL_FN_QUERY_MAP_NAME equ 2 ; Получить имя mapping
|
|
||||||
SHFL_FN_CREATE equ 5 ; Открыть/создать файл
|
|
||||||
SHFL_FN_CLOSE equ 6 ; Закрыть файл
|
|
||||||
SHFL_FN_READ equ 7 ; Читать из файла
|
|
||||||
SHFL_FN_WRITE equ 8 ; Писать в файл
|
|
||||||
SHFL_FN_LIST equ 9 ; Список файлов в директории
|
|
||||||
SHFL_FN_READLINK equ 10 ; Чтение symlink
|
|
||||||
SHFL_FN_INFORMATION equ 11 ; Информация о файле
|
|
||||||
SHFL_FN_REMOVE equ 13 ; Удалить файл
|
|
||||||
SHFL_FN_MAP_FOLDER equ 14 ; Подключить mapping
|
|
||||||
SHFL_FN_UNMAP_FOLDER equ 15 ; Отключить mapping
|
|
||||||
SHFL_FN_RENAME equ 16 ; Переименовать
|
|
||||||
SHFL_FN_FLUSH equ 17 ; Flush буферов
|
|
||||||
|
|
||||||
; Флаги создания/открытия файла
|
|
||||||
SHFL_CF_LOOKUP equ 0x00000001 ; Только lookup (не создавать)
|
|
||||||
SHFL_CF_DIRECTORY equ 0x00000004 ; Это директория
|
|
||||||
SHFL_CF_ACCESS_READ equ 0x00000001 ; Чтение
|
|
||||||
SHFL_CF_ACCESS_WRITE equ 0x00000002 ; Запись
|
|
||||||
SHFL_CF_ACCESS_READWRITE equ 0x00000003 ; Чтение + запись
|
|
||||||
SHFL_CF_ACCESS_ALL equ 0x00000003
|
|
||||||
SHFL_CF_ACCESS_ATTR_READ equ 0x00000004 ; Чтение атрибутов
|
|
||||||
SHFL_CF_ACCESS_ATTR_WRITE equ 0x00000008 ; Запись атрибутов
|
|
||||||
SHFL_CF_ACCESS_MASK equ 0x0000000F
|
|
||||||
|
|
||||||
; Результаты операции создания
|
|
||||||
SHFL_FILE_EXISTS equ 0 ; Файл уже существует
|
|
||||||
SHFL_FILE_CREATED equ 1 ; Файл создан
|
|
||||||
SHFL_FILE_REPLACED equ 2 ; Файл заменен
|
|
||||||
SHFL_PATH_NOT_FOUND equ 3 ; Путь не найден
|
|
||||||
|
|
||||||
; Флаги для LIST операции
|
|
||||||
SHFL_LIST_NONE equ 0x00000000
|
|
||||||
SHFL_LIST_RETURN_ONE equ 0x00000001 ; Вернуть только один элемент
|
|
||||||
|
|
||||||
; Максимальные размеры
|
|
||||||
SHFL_MAX_MAPPINGS equ 10 ; Максимум mappings
|
|
||||||
SHFL_MAX_NAME_LEN equ 256 ; Максимум длина имени
|
|
||||||
SHFL_MAX_PATH_LEN equ 4096; Максимум длина пути
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; KolibriOS Disk Status Codes
|
|
||||||
; =============================================================================
|
|
||||||
DISK_STATUS_GENERAL_ERROR equ -1
|
|
||||||
DISK_STATUS_OK equ 0
|
|
||||||
DISK_STATUS_INVALID_CALL equ 1
|
|
||||||
DISK_STATUS_NO_MEDIA equ 2
|
|
||||||
DISK_STATUS_END_OF_MEDIA equ 3
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Clipboard FSM States
|
|
||||||
; =============================================================================
|
|
||||||
CLIPBOARD_STATE_INIT equ 0 ; Not initialized
|
|
||||||
CLIPBOARD_STATE_CONNECTING equ 1 ; HGCM connect in progress
|
|
||||||
CLIPBOARD_STATE_READY equ 2 ; Ready to process events
|
|
||||||
@@ -1,459 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; Encoding Conversion - UTF-8 ↔ UTF-16LE
|
|
||||||
; Based on Linux VirtualBox Guest Additions implementation
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: utf8_to_utf16le
|
|
||||||
; Конвертация UTF-8 в UTF-16LE (как требует VirtualBox)
|
|
||||||
; Вход:
|
|
||||||
; utf8_ptr - указатель на UTF-8 строку
|
|
||||||
; utf8_len - длина UTF-8 строки в байтах (без null-terminator)
|
|
||||||
; utf16_ptr - указатель на выходной буфер UTF-16LE
|
|
||||||
; utf16_max - максимальный размер выходного буфера в байтах
|
|
||||||
; Возврат:
|
|
||||||
; EAX = количество байт записанных в UTF-16LE (включая BOM если есть)
|
|
||||||
; или -1 при ошибке
|
|
||||||
; =============================================================================
|
|
||||||
proc utf8_to_utf16le uses ebx ecx edx esi edi, \
|
|
||||||
utf8_ptr:dword, utf8_len:dword, utf16_ptr:dword, utf16_max:dword
|
|
||||||
|
|
||||||
DEBUGF 2, "[enc] >>> utf8_to_utf16le(src=0x%x, len=%d, dst=0x%x, max=%d)\n", \
|
|
||||||
[utf8_ptr], [utf8_len], [utf16_ptr], [utf16_max]
|
|
||||||
|
|
||||||
mov esi, [utf8_ptr]
|
|
||||||
test esi, esi
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
mov edi, [utf16_ptr]
|
|
||||||
test edi, edi
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
mov ecx, [utf8_len]
|
|
||||||
test ecx, ecx
|
|
||||||
jz .add_null
|
|
||||||
|
|
||||||
mov edx, [utf16_max]
|
|
||||||
cmp edx, 4 ; Минимум для BOM + null
|
|
||||||
jb .error
|
|
||||||
|
|
||||||
push edi ; Сохраним начало буфера
|
|
||||||
|
|
||||||
; Добавляем UTF-16LE BOM (0xFEFF)
|
|
||||||
mov word [edi], 0xFEFF
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
|
|
||||||
.loop:
|
|
||||||
test ecx, ecx
|
|
||||||
jz .add_null
|
|
||||||
|
|
||||||
cmp edx, 4 ; Нужно место для символа + null
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
; Читаем первый байт UTF-8
|
|
||||||
movzx eax, byte [esi]
|
|
||||||
inc esi
|
|
||||||
dec ecx
|
|
||||||
|
|
||||||
; Проверяем тип символа
|
|
||||||
cmp al, 0x80
|
|
||||||
jb .ascii ; 0xxxxxxx - ASCII (1 байт)
|
|
||||||
|
|
||||||
cmp al, 0xE0
|
|
||||||
jb .two_byte ; 110xxxxx - 2 байта
|
|
||||||
|
|
||||||
cmp al, 0xF0
|
|
||||||
jb .three_byte ; 1110xxxx - 3 байта
|
|
||||||
|
|
||||||
cmp al, 0xF8
|
|
||||||
jb .four_byte ; 11110xxx - 4 байта
|
|
||||||
|
|
||||||
; Неправильный UTF-8 - заменяем на replacement character
|
|
||||||
mov word [edi], 0xFFFD
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.ascii:
|
|
||||||
; ASCII символ - прямая конвертация
|
|
||||||
mov word [edi], ax
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.two_byte:
|
|
||||||
; 110xxxxx 10xxxxxx
|
|
||||||
test ecx, ecx
|
|
||||||
jz .incomplete
|
|
||||||
|
|
||||||
and eax, 0x1F ; Берем 5 бит
|
|
||||||
shl eax, 6
|
|
||||||
|
|
||||||
movzx ebx, byte [esi]
|
|
||||||
inc esi
|
|
||||||
dec ecx
|
|
||||||
|
|
||||||
and ebx, 0x3F ; Берем 6 бит
|
|
||||||
or eax, ebx
|
|
||||||
|
|
||||||
mov word [edi], ax
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.three_byte:
|
|
||||||
; 1110xxxx 10xxxxxx 10xxxxxx
|
|
||||||
cmp ecx, 2
|
|
||||||
jb .incomplete
|
|
||||||
|
|
||||||
and eax, 0x0F ; Берем 4 бита
|
|
||||||
shl eax, 12
|
|
||||||
|
|
||||||
movzx ebx, byte [esi]
|
|
||||||
inc esi
|
|
||||||
dec ecx
|
|
||||||
and ebx, 0x3F
|
|
||||||
shl ebx, 6
|
|
||||||
or eax, ebx
|
|
||||||
|
|
||||||
movzx ebx, byte [esi]
|
|
||||||
inc esi
|
|
||||||
dec ecx
|
|
||||||
and ebx, 0x3F
|
|
||||||
or eax, ebx
|
|
||||||
|
|
||||||
mov word [edi], ax
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.four_byte:
|
|
||||||
; 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
|
||||||
; Это символ вне BMP - нужна surrogate pair
|
|
||||||
cmp ecx, 3
|
|
||||||
jb .incomplete
|
|
||||||
|
|
||||||
cmp edx, 6 ; Нужно 4 байта для surrogate pair + null
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
and eax, 0x07 ; Берем 3 бита
|
|
||||||
shl eax, 18
|
|
||||||
|
|
||||||
movzx ebx, byte [esi]
|
|
||||||
inc esi
|
|
||||||
dec ecx
|
|
||||||
and ebx, 0x3F
|
|
||||||
shl ebx, 12
|
|
||||||
or eax, ebx
|
|
||||||
|
|
||||||
movzx ebx, byte [esi]
|
|
||||||
inc esi
|
|
||||||
dec ecx
|
|
||||||
and ebx, 0x3F
|
|
||||||
shl ebx, 6
|
|
||||||
or eax, ebx
|
|
||||||
|
|
||||||
movzx ebx, byte [esi]
|
|
||||||
inc esi
|
|
||||||
dec ecx
|
|
||||||
and ebx, 0x3F
|
|
||||||
or eax, ebx
|
|
||||||
|
|
||||||
; Преобразуем в surrogate pair
|
|
||||||
sub eax, 0x10000
|
|
||||||
|
|
||||||
; High surrogate: 0xD800 + (code >> 10)
|
|
||||||
mov ebx, eax
|
|
||||||
shr ebx, 10
|
|
||||||
add ebx, 0xD800
|
|
||||||
mov word [edi], bx
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
|
|
||||||
; Low surrogate: 0xDC00 + (code & 0x3FF)
|
|
||||||
and eax, 0x3FF
|
|
||||||
add eax, 0xDC00
|
|
||||||
mov word [edi], ax
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.incomplete:
|
|
||||||
; Неполная последовательность - заменяем на replacement
|
|
||||||
mov word [edi], 0xFFFD
|
|
||||||
add edi, 2
|
|
||||||
sub edx, 2
|
|
||||||
jmp .add_null
|
|
||||||
|
|
||||||
.add_null:
|
|
||||||
; Добавляем null-terminator
|
|
||||||
cmp edx, 2
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
mov word [edi], 0
|
|
||||||
add edi, 2
|
|
||||||
|
|
||||||
; Вычисляем размер
|
|
||||||
pop eax ; Начало буфера
|
|
||||||
sub edi, eax
|
|
||||||
mov eax, edi
|
|
||||||
|
|
||||||
DEBUGF 2, "[enc] Converted: %d bytes UTF-16LE (with BOM)\n", eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.buffer_full:
|
|
||||||
pop eax ; Cleanup stack
|
|
||||||
DEBUGF 1, "[enc] ERROR: Output buffer too small\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[enc] ERROR: Invalid parameters\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: utf16le_to_utf8
|
|
||||||
; Конвертация UTF-16LE в UTF-8
|
|
||||||
; Вход:
|
|
||||||
; utf16_ptr - указатель на UTF-16LE строку
|
|
||||||
; utf16_len - длина UTF-16LE в байтах (включая BOM если есть)
|
|
||||||
; utf8_ptr - указатель на выходной буфер UTF-8
|
|
||||||
; utf8_max - максимальный размер выходного буфера в байтах
|
|
||||||
; Возврат:
|
|
||||||
; EAX = количество байт записанных в UTF-8 (без null-terminator)
|
|
||||||
; или -1 при ошибке
|
|
||||||
; =============================================================================
|
|
||||||
proc utf16le_to_utf8 uses ebx ecx edx esi edi, \
|
|
||||||
utf16_ptr:dword, utf16_len:dword, utf8_ptr:dword, utf8_max:dword
|
|
||||||
|
|
||||||
DEBUGF 2, "[enc] >>> utf16le_to_utf8(src=0x%x, len=%d, dst=0x%x, max=%d)\n", \
|
|
||||||
[utf16_ptr], [utf16_len], [utf8_ptr], [utf8_max]
|
|
||||||
|
|
||||||
mov esi, [utf16_ptr]
|
|
||||||
test esi, esi
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
mov edi, [utf8_ptr]
|
|
||||||
test edi, edi
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
mov ecx, [utf16_len]
|
|
||||||
test ecx, ecx
|
|
||||||
jz .add_null
|
|
||||||
|
|
||||||
; Длина должна быть четной
|
|
||||||
test ecx, 1
|
|
||||||
jnz .error
|
|
||||||
|
|
||||||
mov edx, [utf8_max]
|
|
||||||
test edx, edx
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
push edi ; Сохраним начало буфера
|
|
||||||
|
|
||||||
; Проверяем и пропускаем BOM если есть
|
|
||||||
cmp word [esi], 0xFEFF
|
|
||||||
jne .no_bom
|
|
||||||
|
|
||||||
add esi, 2
|
|
||||||
sub ecx, 2
|
|
||||||
|
|
||||||
.no_bom:
|
|
||||||
shr ecx, 1 ; Конвертируем байты в UTF-16 символы
|
|
||||||
|
|
||||||
.loop:
|
|
||||||
test ecx, ecx
|
|
||||||
jz .add_null
|
|
||||||
|
|
||||||
cmp edx, 4 ; Минимум для UTF-8 символа (макс 4 байта)
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
; Читаем UTF-16 символ (little-endian)
|
|
||||||
movzx eax, word [esi]
|
|
||||||
add esi, 2
|
|
||||||
dec ecx
|
|
||||||
|
|
||||||
; Проверяем является ли это high surrogate
|
|
||||||
cmp ax, 0xD800
|
|
||||||
jb .not_surrogate
|
|
||||||
cmp ax, 0xDBFF
|
|
||||||
ja .check_low_surrogate
|
|
||||||
|
|
||||||
; High surrogate - нужна low surrogate
|
|
||||||
test ecx, ecx
|
|
||||||
jz .incomplete
|
|
||||||
|
|
||||||
movzx ebx, word [esi]
|
|
||||||
add esi, 2
|
|
||||||
dec ecx
|
|
||||||
|
|
||||||
; Проверяем low surrogate
|
|
||||||
cmp bx, 0xDC00
|
|
||||||
jb .invalid_surrogate
|
|
||||||
cmp bx, 0xDFFF
|
|
||||||
ja .invalid_surrogate
|
|
||||||
|
|
||||||
; Комбинируем surrogates
|
|
||||||
sub eax, 0xD800
|
|
||||||
shl eax, 10
|
|
||||||
sub ebx, 0xDC00
|
|
||||||
add eax, ebx
|
|
||||||
add eax, 0x10000
|
|
||||||
jmp .encode_utf8
|
|
||||||
|
|
||||||
.check_low_surrogate:
|
|
||||||
cmp ax, 0xDC00
|
|
||||||
jb .not_surrogate
|
|
||||||
cmp ax, 0xDFFF
|
|
||||||
ja .not_surrogate
|
|
||||||
|
|
||||||
; Одиночная low surrogate - ошибка
|
|
||||||
jmp .invalid_surrogate
|
|
||||||
|
|
||||||
.not_surrogate:
|
|
||||||
; Обычный BMP символ
|
|
||||||
|
|
||||||
.encode_utf8:
|
|
||||||
; EAX содержит Unicode code point
|
|
||||||
|
|
||||||
cmp eax, 0x80
|
|
||||||
jb .utf8_1byte
|
|
||||||
|
|
||||||
cmp eax, 0x800
|
|
||||||
jb .utf8_2byte
|
|
||||||
|
|
||||||
cmp eax, 0x10000
|
|
||||||
jb .utf8_3byte
|
|
||||||
|
|
||||||
; 4-byte UTF-8
|
|
||||||
cmp edx, 4
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
mov ebx, eax
|
|
||||||
shr ebx, 18
|
|
||||||
and ebx, 0x07
|
|
||||||
or bl, 0xF0
|
|
||||||
mov byte [edi], bl
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
|
|
||||||
mov ebx, eax
|
|
||||||
shr ebx, 12
|
|
||||||
and ebx, 0x3F
|
|
||||||
or bl, 0x80
|
|
||||||
mov byte [edi], bl
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
|
|
||||||
mov ebx, eax
|
|
||||||
shr ebx, 6
|
|
||||||
and ebx, 0x3F
|
|
||||||
or bl, 0x80
|
|
||||||
mov byte [edi], bl
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
|
|
||||||
and eax, 0x3F
|
|
||||||
or al, 0x80
|
|
||||||
mov byte [edi], al
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.utf8_3byte:
|
|
||||||
cmp edx, 3
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
mov ebx, eax
|
|
||||||
shr ebx, 12
|
|
||||||
and ebx, 0x0F
|
|
||||||
or bl, 0xE0
|
|
||||||
mov byte [edi], bl
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
|
|
||||||
mov ebx, eax
|
|
||||||
shr ebx, 6
|
|
||||||
and ebx, 0x3F
|
|
||||||
or bl, 0x80
|
|
||||||
mov byte [edi], bl
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
|
|
||||||
and eax, 0x3F
|
|
||||||
or al, 0x80
|
|
||||||
mov byte [edi], al
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.utf8_2byte:
|
|
||||||
cmp edx, 2
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
mov ebx, eax
|
|
||||||
shr ebx, 6
|
|
||||||
and ebx, 0x1F
|
|
||||||
or bl, 0xC0
|
|
||||||
mov byte [edi], bl
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
|
|
||||||
and eax, 0x3F
|
|
||||||
or al, 0x80
|
|
||||||
mov byte [edi], al
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.utf8_1byte:
|
|
||||||
; ASCII
|
|
||||||
mov byte [edi], al
|
|
||||||
inc edi
|
|
||||||
dec edx
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.invalid_surrogate:
|
|
||||||
.incomplete:
|
|
||||||
; Замена на replacement character (U+FFFD = EF BF BD в UTF-8)
|
|
||||||
cmp edx, 3
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
mov byte [edi], 0xEF
|
|
||||||
mov byte [edi+1], 0xBF
|
|
||||||
mov byte [edi+2], 0xBD
|
|
||||||
add edi, 3
|
|
||||||
sub edx, 3
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.add_null:
|
|
||||||
; Добавляем null-terminator
|
|
||||||
cmp edx, 1
|
|
||||||
jb .buffer_full
|
|
||||||
|
|
||||||
mov byte [edi], 0
|
|
||||||
inc edi
|
|
||||||
|
|
||||||
; Вычисляем размер (без null)
|
|
||||||
pop eax ; Начало буфера
|
|
||||||
sub edi, eax
|
|
||||||
dec edi ; Не считаем null
|
|
||||||
mov eax, edi
|
|
||||||
|
|
||||||
DEBUGF 2, "[enc] Converted: %d bytes UTF-8\n", eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.buffer_full:
|
|
||||||
pop eax ; Cleanup stack
|
|
||||||
DEBUGF 1, "[enc] ERROR: Output buffer too small\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[enc] ERROR: Invalid parameters\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
@@ -1,272 +1,82 @@
|
|||||||
; =============================================================================
|
; =============================================================================
|
||||||
; VBoxGuest Driver для KolibriOS
|
; VBoxGuest Driver for KolibriOS - Dual-level Error Handling
|
||||||
; Модуль : Коды ошибок и обработка ошибок
|
; VMMDev transport errors + HGCM service errors
|
||||||
; Назначение : Определения кодов ошибок VirtualBox и функции их обработки
|
; На базе версии v1_04
|
||||||
; Источник : VBox/err.h, VBoxVideoErr.h
|
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
|
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
; Коды успеха (>= 0)
|
; VMMDev Error Codes (Generic VERR_*)
|
||||||
; =============================================================================
|
; These appear in vmmdev_request_header.rc
|
||||||
VINF_SUCCESS equ 0 ; Успешное выполнение
|
|
||||||
VINF_HGCM_ASYNC_EXECUTE equ 2903 ; HGCM выполняется асинхронно
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Общие коды ошибок (транспортный уровень VMMDev)
|
|
||||||
; Диапазон: -1 .. -999
|
|
||||||
; =============================================================================
|
|
||||||
VERR_GENERAL_FAILURE equ -1 ; Общая ошибка
|
|
||||||
VERR_INVALID_PARAMETER equ -2 ; Неверный параметр
|
|
||||||
VERR_INVALID_MAGIC equ -3 ; Неверная сигнатура
|
|
||||||
VERR_INVALID_HANDLE equ -4 ; Неверный handle
|
|
||||||
VERR_LOCK_FAILED equ -5 ; Ошибка блокировки
|
|
||||||
VERR_INVALID_POINTER equ -6 ; Неверный указатель
|
|
||||||
VERR_IDT_FAILED equ -7 ; Ошибка IDT
|
|
||||||
VERR_NO_MEMORY equ -8 ; Недостаточно памяти
|
|
||||||
VERR_ALREADY_LOADED equ -9 ; Уже загружен
|
|
||||||
VERR_PERMISSION_DENIED equ -10 ; Доступ запрещён
|
|
||||||
VERR_TIMEOUT equ -11 ; Таймаут операции
|
|
||||||
VERR_NOT_IMPLEMENTED equ -12 ; Не реализовано
|
|
||||||
VERR_NOT_EQUAL equ -18 ; Не равно
|
|
||||||
VERR_INTERRUPTED equ -39 ; Операция прервана
|
|
||||||
VERR_TOO_MUCH_DATA equ -42 ; Слишком много данных
|
|
||||||
VERR_NOT_FOUND equ -78 ; Не найдено
|
|
||||||
VERR_INVALID_STATE equ -79 ; Неверное состояние
|
|
||||||
VERR_OUT_OF_RESOURCES equ -80 ; Ресурсы исчерпаны
|
|
||||||
VERR_ALREADY_EXISTS equ -105 ; Уже существует
|
|
||||||
VERR_RESOURCE_BUSY equ -138 ; Ресурс занят (0xFFFFFF76)
|
|
||||||
VERR_INTERNAL_ERROR equ -225 ; Внутренняя ошибка
|
|
||||||
VERR_INVALID_FUNCTION equ -36 ; Неверная функция
|
|
||||||
VERR_NOT_SUPPORTED equ -37 ; Не поддерживается
|
|
||||||
VERR_ACCESS_DENIED equ -38 ; Доступ запрещён
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Коды ошибок HGCM (уровень сервисов)
|
|
||||||
; Диапазон: -2900 .. -2999
|
|
||||||
; =============================================================================
|
|
||||||
VERR_HGCM_SERVICE_NOT_FOUND equ -2900 ; Сервис не найден
|
|
||||||
VERR_HGCM_CLIENT_REJECTED equ -2901 ; Клиент отклонён
|
|
||||||
VERR_HGCM_INVALID_CMD_ADDRESS equ -2902 ; Неверный адрес команды
|
|
||||||
VERR_HGCM_INTERNAL equ -2904 ; Внутренняя ошибка HGCM
|
|
||||||
VERR_HGCM_INVALID_CLIENT_ID equ -2905 ; Неверный client ID
|
|
||||||
VERR_HGCM_PROTOCOL_ERROR equ -2906 ; Ошибка протокола
|
|
||||||
VERR_HGCM_TOO_MANY_CLIENTS equ -2908 ; Слишком много клиентов
|
|
||||||
VERR_HGCM_TOO_MANY_PARMS equ -2909 ; Слишком много параметров
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Коды ошибок Clipboard
|
|
||||||
; Диапазон: -7150 .. -7199
|
|
||||||
; =============================================================================
|
|
||||||
VERR_SHCLPB_NO_DATA equ -7153 ; Нет данных в буфере обмена
|
|
||||||
VERR_SHCLPB_FORMAT_NOT_SUPPORTED equ -7154 ; Формат не поддерживается
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Строки сообщений об ошибках
|
|
||||||
; =============================================================================
|
|
||||||
align 4
|
|
||||||
|
|
||||||
; Успех
|
|
||||||
msg_success db "Success", 0
|
|
||||||
msg_async_pending db "Async operation pending", 0
|
|
||||||
|
|
||||||
; Общие ошибки
|
|
||||||
msg_general_failure db "General failure", 0
|
|
||||||
msg_invalid_parameter db "Invalid parameter", 0
|
|
||||||
msg_invalid_pointer db "Invalid pointer", 0
|
|
||||||
msg_no_memory db "Out of memory", 0
|
|
||||||
msg_timeout db "Operation timed out", 0
|
|
||||||
msg_not_implemented db "Not implemented", 0
|
|
||||||
msg_invalid_function db "Invalid function", 0
|
|
||||||
msg_not_supported db "Not supported", 0
|
|
||||||
msg_access_denied db "Access denied", 0
|
|
||||||
msg_too_much_data db "Too much data", 0
|
|
||||||
msg_not_found db "Not found", 0
|
|
||||||
msg_invalid_state db "Invalid state", 0
|
|
||||||
msg_out_of_resources db "Out of resources", 0
|
|
||||||
msg_already_exists db "Already exists", 0
|
|
||||||
msg_internal_error db "Internal error", 0
|
|
||||||
msg_interrupted db "Operation interrupted", 0
|
|
||||||
msg_resource_busy db "Resource busy", 0
|
|
||||||
|
|
||||||
; Ошибки HGCM
|
|
||||||
msg_hgcm_service_not_found db "HGCM service not found", 0
|
|
||||||
msg_hgcm_client_rejected db "HGCM client rejected", 0
|
|
||||||
msg_hgcm_invalid_cmd db "Invalid HGCM command address", 0
|
|
||||||
msg_hgcm_internal db "HGCM internal error", 0
|
|
||||||
msg_hgcm_invalid_client db "Invalid HGCM client ID", 0
|
|
||||||
msg_hgcm_protocol_error db "HGCM protocol error", 0
|
|
||||||
msg_hgcm_too_many_clients db "Too many HGCM clients", 0
|
|
||||||
msg_hgcm_too_many_parms db "Too many HGCM parameters", 0
|
|
||||||
|
|
||||||
; Ошибки Clipboard
|
|
||||||
msg_shclpb_no_data db "No clipboard data", 0
|
|
||||||
msg_shclpb_format_not_supported db "Clipboard format not supported", 0
|
|
||||||
|
|
||||||
; Неизвестная ошибка
|
|
||||||
msg_unknown_error db "Unknown error", 0
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Таблица соответствия кодов ошибок и сообщений
|
|
||||||
; =============================================================================
|
|
||||||
struct ERROR_ENTRY
|
|
||||||
code dd ? ; Код ошибки
|
|
||||||
message dd ? ; Указатель на строку сообщения
|
|
||||||
ends
|
|
||||||
|
|
||||||
align 4
|
|
||||||
error_table:
|
|
||||||
; Коды успеха
|
|
||||||
dd VINF_SUCCESS, msg_success
|
|
||||||
dd VINF_HGCM_ASYNC_EXECUTE, msg_async_pending
|
|
||||||
|
|
||||||
; Общие ошибки
|
|
||||||
dd VERR_GENERAL_FAILURE, msg_general_failure
|
|
||||||
dd VERR_INVALID_PARAMETER, msg_invalid_parameter
|
|
||||||
dd VERR_INVALID_POINTER, msg_invalid_pointer
|
|
||||||
dd VERR_NO_MEMORY, msg_no_memory
|
|
||||||
dd VERR_TIMEOUT, msg_timeout
|
|
||||||
dd VERR_NOT_IMPLEMENTED, msg_not_implemented
|
|
||||||
dd VERR_INVALID_FUNCTION, msg_invalid_function
|
|
||||||
dd VERR_NOT_SUPPORTED, msg_not_supported
|
|
||||||
dd VERR_ACCESS_DENIED, msg_access_denied
|
|
||||||
dd VERR_TOO_MUCH_DATA, msg_too_much_data
|
|
||||||
dd VERR_NOT_FOUND, msg_not_found
|
|
||||||
dd VERR_INVALID_STATE, msg_invalid_state
|
|
||||||
dd VERR_OUT_OF_RESOURCES, msg_out_of_resources
|
|
||||||
dd VERR_ALREADY_EXISTS, msg_already_exists
|
|
||||||
dd VERR_INTERNAL_ERROR, msg_internal_error
|
|
||||||
dd VERR_INTERRUPTED, msg_interrupted
|
|
||||||
dd VERR_RESOURCE_BUSY, msg_resource_busy
|
|
||||||
|
|
||||||
; Ошибки HGCM
|
|
||||||
dd VERR_HGCM_SERVICE_NOT_FOUND, msg_hgcm_service_not_found
|
|
||||||
dd VERR_HGCM_CLIENT_REJECTED, msg_hgcm_client_rejected
|
|
||||||
dd VERR_HGCM_INVALID_CMD_ADDRESS, msg_hgcm_invalid_cmd
|
|
||||||
dd VERR_HGCM_INTERNAL, msg_hgcm_internal
|
|
||||||
dd VERR_HGCM_INVALID_CLIENT_ID, msg_hgcm_invalid_client
|
|
||||||
dd VERR_HGCM_PROTOCOL_ERROR, msg_hgcm_protocol_error
|
|
||||||
dd VERR_HGCM_TOO_MANY_CLIENTS, msg_hgcm_too_many_clients
|
|
||||||
dd VERR_HGCM_TOO_MANY_PARMS, msg_hgcm_too_many_parms
|
|
||||||
|
|
||||||
; Ошибки Clipboard
|
|
||||||
dd VERR_SHCLPB_NO_DATA, msg_shclpb_no_data
|
|
||||||
dd VERR_SHCLPB_FORMAT_NOT_SUPPORTED, msg_shclpb_format_not_supported
|
|
||||||
|
|
||||||
error_table_end:
|
|
||||||
error_table_count equ (error_table_end - error_table) / 8
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: error_get_message
|
|
||||||
; Назначение: Получение человеко-читаемого сообщения по коду ошибки
|
|
||||||
; Вход : EAX = код ошибки
|
|
||||||
; Выход: EAX = указатель на строку сообщения
|
|
||||||
; =============================================================================
|
|
||||||
proc error_get_message uses ebx ecx edi
|
|
||||||
mov ebx, eax ; Сохраняем код ошибки
|
|
||||||
mov edi, error_table ; Указатель на таблицу
|
|
||||||
mov ecx, error_table_count ; Количество записей
|
|
||||||
|
|
||||||
.loop:
|
|
||||||
cmp dword [edi], ebx ; Сравниваем код
|
|
||||||
je .found
|
|
||||||
add edi, 8 ; Следующая запись
|
|
||||||
loop .loop
|
|
||||||
|
|
||||||
; Ошибка не найдена
|
|
||||||
mov eax, msg_unknown_error
|
|
||||||
ret
|
|
||||||
|
|
||||||
.found:
|
|
||||||
mov eax, [edi + 4] ; Возвращаем указатель на сообщение
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: error_is_success
|
|
||||||
; Назначение: Проверка, является ли код успешным
|
|
||||||
; Вход : EAX = код
|
|
||||||
; Выход: EAX = 0 если успех, != 0 если ошибка
|
|
||||||
; ZF = 1 если успех
|
|
||||||
; =============================================================================
|
|
||||||
proc error_is_success
|
|
||||||
test eax, eax
|
|
||||||
jns .success ; Если >= 0, это успех
|
|
||||||
|
|
||||||
; Это ошибка
|
|
||||||
or eax, 1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.success:
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: error_is_hgcm
|
|
||||||
; Назначение: Проверка, является ли ошибка HGCM-специфичной
|
|
||||||
; Вход : EAX = код ошибки
|
|
||||||
; Выход: EAX = 0 если HGCM ошибка, != 0 в противном случае
|
|
||||||
; ZF = 1 если HGCM ошибка
|
|
||||||
; =============================================================================
|
|
||||||
proc error_is_hgcm
|
|
||||||
cmp eax, -2910
|
|
||||||
jl .not_hgcm
|
|
||||||
cmp eax, -2900
|
|
||||||
jg .not_hgcm
|
|
||||||
|
|
||||||
xor eax, eax ; Это HGCM ошибка
|
|
||||||
ret
|
|
||||||
|
|
||||||
.not_hgcm:
|
|
||||||
or eax, 1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; МАКРОСЫ ДЛЯ ПРОВЕРКИ ОШИБОК
|
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
|
|
||||||
; Проверка кода возврата VMMDev и переход при ошибке
|
; Success codes (positive or zero)
|
||||||
macro CHECK_VMMDEV_RC label_error {
|
VINF_SUCCESS equ 0
|
||||||
stdcall error_is_success
|
VINF_HGCM_ASYNC_EXECUTE equ 2903 ; Запрос выполняется асинхронно
|
||||||
jnz label_error
|
VBOX_HGCM_REQ_DONE equ 0x00000001 ; Флаг завершения в поле flags
|
||||||
}
|
HGCM_TIMEOUT_DEFAULT equ 500000 ; Базовый таймаут
|
||||||
|
|
||||||
; Проверка результата HGCM и переход при ошибке
|
; err.h
|
||||||
macro CHECK_HGCM_RESULT ptr, label_error {
|
VINF_NOT_SUPPORTED equ 37
|
||||||
mov eax, [ptr + HGCM_HEADER.result]
|
VERR_ACCESS_DENIED equ -38
|
||||||
stdcall error_is_success
|
VERR_INTERRUPTED equ -39
|
||||||
jnz label_error
|
VINF_INTERRUPTED equ 39
|
||||||
}
|
|
||||||
|
|
||||||
; Проверка EAX на ошибку (отрицательное значение)
|
; =============================================================================
|
||||||
macro CHECK_ERROR label_error {
|
; Error Codes
|
||||||
test eax, eax
|
; =============================================================================
|
||||||
js label_error
|
VERR_TIMEOUT equ -78
|
||||||
}
|
VERR_NOT_READY equ -25
|
||||||
|
|
||||||
; Проверка успеха и переход при ошибке
|
VERR_GENERAL_FAILURE equ -1
|
||||||
macro CHECK_SUCCESS label_error {
|
VERR_INVALID_PARAMETER equ -2
|
||||||
call error_is_success
|
VERR_INVALID_MAGIC equ -3
|
||||||
jnz label_error
|
VERR_INVALID_POINTER equ -6
|
||||||
}
|
VERR_NO_MEMORY equ -8
|
||||||
|
VERR_NOT_IMPLEMENTED equ -12
|
||||||
|
VERR_INVALID_FLAGS equ -13 ; 0xFFFFFFF3
|
||||||
|
VERR_INVALID_FUNCTION equ -36
|
||||||
|
VERR_NOT_SUPPORTED equ -37
|
||||||
|
VERR_TOO_MUCH_DATA equ -42
|
||||||
|
VERR_NOT_FOUND equ -78
|
||||||
|
VERR_INVALID_STATE equ -79
|
||||||
|
VERR_OUT_OF_RESOURCES equ -80
|
||||||
|
VERR_ALREADY_EXISTS equ -105
|
||||||
|
VERR_TRY_AGAIN equ -116
|
||||||
|
VERR_INTERNAL_ERROR equ -225
|
||||||
|
|
||||||
; Вывод сообщения об ошибке
|
; VERR_WRONG_TYPE-22409
|
||||||
macro PRINT_ERROR {
|
; VERR_WRONG_PARAMETER_TYPE-22416
|
||||||
push eax
|
|
||||||
call error_get_message
|
VERR_NO_DATA equ -125 ; Нэт доступных данных
|
||||||
invoke SysMsgBoardStr, eax
|
|
||||||
pop eax
|
|
||||||
}
|
; =============================================================================
|
||||||
|
; HGCM Error Codes (Range -2900..-2909)
|
||||||
|
; These appear in VMMDevHGCMRequestHeader.result
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
VERR_HGCM_SERVICE_NOT_FOUND equ -2900
|
||||||
|
VERR_HGCM_CLIENT_REJECTED equ -2901
|
||||||
|
VERR_HGCM_INVALID_CMD_ADDRESS equ -2902
|
||||||
|
VERR_HGCM_INTERNAL equ -2904
|
||||||
|
VERR_HGCM_INVALID_CLIENT_ID equ -2905
|
||||||
|
VERR_HGCM_PROTOCOL_ERROR equ -2906
|
||||||
|
VERR_HGCM_TOO_MANY_CLIENTS equ -2908
|
||||||
|
VERR_HGCM_TOO_MANY_PARMS equ -2909
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Clipboard-specific Error Codes
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
VERR_SHCLPB_NO_DATA equ -7153
|
||||||
|
VERR_SHCLPB_FORMAT_NOT_SUPPORTED equ -7154
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Error Code Ranges
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
VMMDEV_ERROR_RANGE_START equ -1000
|
||||||
|
VMMDEV_ERROR_RANGE_END equ 0
|
||||||
|
HGCM_ERROR_RANGE_START equ -2910
|
||||||
|
HGCM_ERROR_RANGE_END equ -2900
|
||||||
|
|
||||||
; Комбинированная проверка и вывод
|
|
||||||
macro CHECK_AND_PRINT label_error {
|
|
||||||
test eax, eax
|
|
||||||
jns @f
|
|
||||||
push eax
|
|
||||||
call error_get_message
|
|
||||||
invoke SysMsgBoardStr, eax
|
|
||||||
pop eax
|
|
||||||
jmp label_error
|
|
||||||
@@:
|
|
||||||
}
|
|
||||||
174
common/log.inc
174
common/log.inc
@@ -1,174 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; VBoxGuest Driver for KolibriOS - Multi-level Logging System
|
|
||||||
; Supports DEBUG_LEVEL: 1 (production), 2 (development)
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; Log levels:
|
|
||||||
; 1 = CRITICAL - Errors, warnings (production)
|
|
||||||
; 2 = INFO - Detailed operation logs (development)
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Core Logging Macros
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; ; Log at CRITICAL level (always shown if __DEBUG__ = 1)
|
|
||||||
; macro LOG_CRITICAL msg, [args] {
|
|
||||||
; common
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
; DEBUGF 1, "[vbox] " # msg, args
|
|
||||||
; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
; Log at INFO level (shown only if __DEBUG_LEVEL__ >= 2)
|
|
||||||
macro LOG_INFO msg, [args] {
|
|
||||||
common
|
|
||||||
if __DEBUG__ eq 1
|
|
||||||
if __DEBUG_LEVEL__ >= 2
|
|
||||||
DEBUGF 2, "[vbox] " # msg, args
|
|
||||||
end if
|
|
||||||
end if
|
|
||||||
}
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Subsystem-specific Logging
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; ; VMMDev protocol logs
|
|
||||||
; macro LOG_VMMDEV level, msg, [args] {
|
|
||||||
; common
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
; if level <= __DEBUG_LEVEL__
|
|
||||||
; DEBUGF level, "[vmmdev] " # msg, args
|
|
||||||
; end if
|
|
||||||
; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
; ; HGCM protocol logs
|
|
||||||
; macro LOG_HGCM level, msg, [args] {
|
|
||||||
; common
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
; if level <= __DEBUG_LEVEL__
|
|
||||||
; DEBUGF level, "[hgcm] " # msg, args
|
|
||||||
; end if
|
|
||||||
; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
; ; Clipboard service logs
|
|
||||||
; macro LOG_CLIP level, msg, [args] {
|
|
||||||
; common
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
; if level <= __DEBUG_LEVEL__
|
|
||||||
; DEBUGF level, "[clip] " # msg, args
|
|
||||||
; end if
|
|
||||||
; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
; ; Display service logs
|
|
||||||
; macro LOG_DISP level, msg, [args] {
|
|
||||||
; common
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
; if level <= __DEBUG_LEVEL__
|
|
||||||
; DEBUGF level, "[display] " # msg, args
|
|
||||||
; end if
|
|
||||||
; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
; ; IRQ handler logs
|
|
||||||
; macro LOG_IRQ level, msg, [args] {
|
|
||||||
; common
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
; if level <= __DEBUG_LEVEL__
|
|
||||||
; DEBUGF level, "[irq] " # msg, args
|
|
||||||
; end if
|
|
||||||
; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Error Logging with Return Code
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; Log error with return code (always at level 1)
|
|
||||||
macro LOG_ERROR subsystem, operation, rc {
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
push eax esi
|
|
||||||
mov eax, rc
|
|
||||||
stdcall error_get_message, eax
|
|
||||||
mov esi, eax
|
|
||||||
DEBUGF 1, "[" # subsystem # "] " # operation # " failed: %s (rc=0x%x)\n", esi, rc
|
|
||||||
pop esi eax
|
|
||||||
; end if
|
|
||||||
}
|
|
||||||
|
|
||||||
; Log VMMDev error
|
|
||||||
macro LOG_VMMDEV_ERROR operation, rc {
|
|
||||||
LOG_ERROR "vmmdev", operation, rc
|
|
||||||
}
|
|
||||||
|
|
||||||
; Log HGCM error
|
|
||||||
macro LOG_HGCM_ERROR operation, rc {
|
|
||||||
LOG_ERROR "hgcm", operation, rc
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
; ; Trace function exit
|
|
||||||
; macro TRACE_EXIT func_name {
|
|
||||||
; if __DEBUG__ eq 1
|
|
||||||
; ; if __DEBUG_LEVEL__ >= 2
|
|
||||||
; DEBUGF 2, "[trace] <<< %s\n", func_name
|
|
||||||
; ; end if
|
|
||||||
; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
; ; Trace function with return value
|
|
||||||
; macro TRACE_EXIT_RC func_name, rc {
|
|
||||||
; ; if __DEBUG__ eq 1
|
|
||||||
; ; if __DEBUG_LEVEL__ >= 2
|
|
||||||
; DEBUGF 2, "[trace] <<< %s -> 0x%x\n", func_name, rc
|
|
||||||
; ; end if
|
|
||||||
; ; end if
|
|
||||||
; }
|
|
||||||
|
|
||||||
macro LOG_HGCM_PACKET addr {
|
|
||||||
if __DEBUG__ eq 1
|
|
||||||
if __DEBUG_LEVEL__ >= 2
|
|
||||||
push eax ebx ecx edx
|
|
||||||
mov eax, [addr + HGCM_HEADER.header.size]
|
|
||||||
mov ebx, [addr + HGCM_HEADER.header.request_type]
|
|
||||||
mov ecx, [addr + HGCM_HEADER.header.rc]
|
|
||||||
DEBUGF 2, "[hgcm] Packet: size=%d, type=0x%x, rc=0x%x\n", eax, ebx, ecx
|
|
||||||
pop edx ecx ebx eax
|
|
||||||
end if
|
|
||||||
end if
|
|
||||||
}
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Data Dump (DEBUG_LEVEL 2)
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; Dump memory region
|
|
||||||
macro LOG_DUMP_MEMORY addr, size {
|
|
||||||
push eax ebx ecx esi
|
|
||||||
mov esi, addr
|
|
||||||
mov ecx, size
|
|
||||||
DEBUGF 2, "[dump] Memory at 0x%x, size=%d bytes:\n", esi, ecx
|
|
||||||
cmp ecx, 64
|
|
||||||
jbe .dump_all
|
|
||||||
mov ecx, 64
|
|
||||||
.dump_all:
|
|
||||||
test ecx, ecx
|
|
||||||
jz .dump_done
|
|
||||||
xor ebx, ebx
|
|
||||||
.dump_loop:
|
|
||||||
mov al, [esi + ebx]
|
|
||||||
DEBUGF 2, "0x%x ", al
|
|
||||||
inc ebx
|
|
||||||
test ebx, 0x0F
|
|
||||||
jnz .no_newline
|
|
||||||
DEBUGF 2, "\n"
|
|
||||||
.no_newline:
|
|
||||||
cmp ebx, ecx
|
|
||||||
jb .dump_loop
|
|
||||||
DEBUGF 2, "\n"
|
|
||||||
.dump_done:
|
|
||||||
pop esi ecx ebx eax
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; VBoxGuest Driver for KolibriOS - Helper Macros
|
|
||||||
; VBox request sending, VGA control, HGCM packet initialization
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; VirtualBox Request Sending
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
macro vbox_send_pack {
|
|
||||||
movzx edx, word [vbox_device.port]
|
|
||||||
DEBUGF 2, "Sending request: port=0x%x, phys_addr=0x%x\n", edx, eax
|
|
||||||
|
|
||||||
mov dx, [vbox_device.port]
|
|
||||||
out dx, eax
|
|
||||||
|
|
||||||
; Small delay for processing
|
|
||||||
push ecx
|
|
||||||
mov ecx, 1000
|
|
||||||
.wait:
|
|
||||||
in al, 0x80 ; I/O wait
|
|
||||||
loop .wait
|
|
||||||
pop ecx
|
|
||||||
}
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; VGA Control Macros
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
macro vga_write_reg index_val, data_val {
|
|
||||||
mov eax, index_val
|
|
||||||
mov dx, VGA_INDEX_PORT
|
|
||||||
out dx, ax
|
|
||||||
|
|
||||||
mov eax, data_val
|
|
||||||
mov dx, VGA_DATA_PORT
|
|
||||||
out dx, ax
|
|
||||||
}
|
|
||||||
|
|
||||||
macro vga_set_mode w, h, bpp {
|
|
||||||
vga_write_reg VGA_INDEX_ENABLE, VGA_DISABLED
|
|
||||||
vga_write_reg VGA_INDEX_XRES, w
|
|
||||||
vga_write_reg VGA_INDEX_YRES, h
|
|
||||||
vga_write_reg VGA_INDEX_BPP, bpp
|
|
||||||
vga_write_reg VGA_INDEX_ENABLE, VGA_ENABLED or VGA_LFB_ENABLED
|
|
||||||
}
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; HGCM Packet Initialization Macros
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
macro hgcm_init_header ptr, pkt_size, req_type {
|
|
||||||
mov dword [ptr + HGCM_HEADER.header.size], pkt_size
|
|
||||||
mov dword [ptr + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [ptr + HGCM_HEADER.header.request_type], req_type
|
|
||||||
mov dword [ptr + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
|
|
||||||
mov dword [ptr + HGCM_HEADER.header.reserved1], 0
|
|
||||||
mov dword [ptr + HGCM_HEADER.header.reserved2], 0
|
|
||||||
mov dword [ptr + HGCM_HEADER.flags], 0
|
|
||||||
mov dword [ptr + HGCM_HEADER.result], VERR_INTERNAL_ERROR
|
|
||||||
}
|
|
||||||
|
|
||||||
macro hgcm_set_parm_u32 ptr, val {
|
|
||||||
mov dword [ptr + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
|
||||||
mov dword [ptr + HGCM_PARM.value_or_size], val
|
|
||||||
mov dword [ptr + HGCM_PARM.offset_or_addr], 0
|
|
||||||
}
|
|
||||||
|
|
||||||
macro hgcm_set_parm_ptr ptr, psize, paddr {
|
|
||||||
mov dword [ptr + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_IN
|
|
||||||
mov dword [ptr + HGCM_PARM.value_or_size], psize
|
|
||||||
mov dword [ptr + HGCM_PARM.offset_or_addr], paddr
|
|
||||||
}
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Error Checking Macros
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
macro CHECK_VMMDEV_RC label_error {
|
|
||||||
stdcall error_is_success
|
|
||||||
jnz label_error
|
|
||||||
}
|
|
||||||
|
|
||||||
macro CHECK_HGCM_RESULT ptr, label_error {
|
|
||||||
mov eax, [ptr + HGCM_HEADER.result]
|
|
||||||
stdcall error_is_success
|
|
||||||
jnz label_error
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
macro hgcm_set_parm_ptr_out dest, size, addr
|
|
||||||
{
|
|
||||||
mov dword [dest + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_OUT
|
|
||||||
mov dword [dest + HGCM_PARM.value_or_size], size
|
|
||||||
mov dword [dest + HGCM_PARM.offset_or_addr], addr
|
|
||||||
}
|
|
||||||
|
|
||||||
macro hgcm_set_parm32_u32 ptr, val {
|
|
||||||
mov dword [ptr + 0], 1 ; type = 32BIT
|
|
||||||
mov dword [ptr + 4], val ; value32
|
|
||||||
mov dword [ptr + 8], 0 ; unused
|
|
||||||
}
|
|
||||||
|
|
||||||
macro hgcm_set_parm32_linaddr_out ptr, size, addr {
|
|
||||||
mov dword [ptr + 0], 9 ; type = LINADDR_KERNEL_OUT
|
|
||||||
mov dword [ptr + 4], size ; buffer size
|
|
||||||
mov dword [ptr + 8], addr ; linear address
|
|
||||||
}
|
|
||||||
@@ -1,357 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; VBoxGuest Driver for KolibriOS - Data Structures
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; VMMDev Structures
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
struct VMMDEV_HEADER
|
|
||||||
size dd ?
|
|
||||||
version dd ?
|
|
||||||
request_type dd ?
|
|
||||||
rc dd ?
|
|
||||||
reserved1 dd ?
|
|
||||||
reserved2 dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct VMMDEV_GUEST_INFO
|
|
||||||
header VMMDEV_HEADER
|
|
||||||
version dd ?
|
|
||||||
os_type dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct VMMDEV_GUEST_CAPS
|
|
||||||
header VMMDEV_HEADER
|
|
||||||
caps dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct VMMDEV_ACK_EVENTS
|
|
||||||
header VMMDEV_HEADER
|
|
||||||
events dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct VMMDEV_DISPLAY_CHANGE
|
|
||||||
header VMMDEV_HEADER
|
|
||||||
x_res dd ?
|
|
||||||
y_res dd ?
|
|
||||||
bpp dd ?
|
|
||||||
event_ack dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; VMMDev MMIO структура для KolibriOS
|
|
||||||
; =============================================================================
|
|
||||||
struct VMMDEV_MEMORY
|
|
||||||
version dd ? ; 0x00 — версия структуры
|
|
||||||
revision dd ? ; 0x04 — ревизия драйвера
|
|
||||||
reserved1 dd ? ; 0x08 — зарезервировано
|
|
||||||
request_type dd ? ; 0x0C — тип запроса
|
|
||||||
request_result dd ? ; 0x10 — результат запроса
|
|
||||||
|
|
||||||
event_flags dd ? ; 0x14 — pending события от хоста (читаем)
|
|
||||||
event_ack dd ? ; 0x18 — подтверждение событий (пишем)
|
|
||||||
mouse_status dd ? ; 0x1C — статус мыши
|
|
||||||
mouse_data dd ? ; 0x20 — данные мыши
|
|
||||||
mouse_data_last dd ? ; 0x24 — последние данные мыши
|
|
||||||
absolute_mouse dd ? ; 0x28 — абсолютные координаты мыши
|
|
||||||
event_enabled dd ? ; 0x2C — включённые события (R/W)
|
|
||||||
|
|
||||||
padding rb 0xD0 ; 0x100 - 0x30 = 0xD0
|
|
||||||
ends
|
|
||||||
|
|
||||||
; Константы событий
|
|
||||||
VMMDEV_EVENT_MOUSE_POSITION_CHANGED equ 0x00000001
|
|
||||||
VMMDEV_EVENT_MOUSE_CAPTURED equ 0x00000002
|
|
||||||
VMMDEV_EVENT_MOUSE_GUEST_NEEDS_CAPTURE equ 0x00000004
|
|
||||||
VMMDEV_EVENT_MOUSE_GUEST_CAN_CAPTURE equ 0x00000008
|
|
||||||
VMMDEV_EVENT_MOUSE_GUEST_RELEASED equ 0x00000010
|
|
||||||
VMMDEV_EVENT_DISPLAY_CHANGE equ 0x00000020
|
|
||||||
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE equ 0x00000040
|
|
||||||
VMMDEV_EVENT_GRAPHICS_CHANGE equ 0x00000080
|
|
||||||
VMMDEV_EVENT_HGCM equ 0x00000100
|
|
||||||
VMMDEV_EVENT_BALLOON_CHANGE equ 0x00000200
|
|
||||||
VMMDEV_EVENT_STATISTICS_INTERVAL equ 0x00000400
|
|
||||||
VMMDEV_EVENT_VBVA_ENABLED equ 0x00000800
|
|
||||||
VMMDEV_EVENT_HGCM_DRV_UNLOAD equ 0x00001000
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; HGCM Structures
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
struct HGCM_HEADER
|
|
||||||
header VMMDEV_HEADER
|
|
||||||
flags dd ?
|
|
||||||
result dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct HGCM_CONNECT
|
|
||||||
header HGCM_HEADER
|
|
||||||
location_type dd ?
|
|
||||||
service_name rb HGCM_SERVICE_NAME_MAX
|
|
||||||
client_id dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct HGCM_DISCONNECT
|
|
||||||
header HGCM_HEADER
|
|
||||||
client_id dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct HGCM_CALL
|
|
||||||
header HGCM_HEADER
|
|
||||||
client_id dd ?
|
|
||||||
function dd ?
|
|
||||||
param_count dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct HGCM_PARM
|
|
||||||
type dd ?
|
|
||||||
value_or_size dd ?
|
|
||||||
offset_or_addr dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Clipboard Structures
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
struct SHCL_MSG_GET
|
|
||||||
header HGCM_CALL
|
|
||||||
msg_type HGCM_PARM
|
|
||||||
formats HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHCL_FORMATS_REPORT
|
|
||||||
header HGCM_CALL
|
|
||||||
formats HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHCL_DATA_READ
|
|
||||||
header HGCM_CALL
|
|
||||||
format HGCM_PARM
|
|
||||||
buffer HGCM_PARM
|
|
||||||
size HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHCL_DATA_WRITE
|
|
||||||
header HGCM_CALL
|
|
||||||
format HGCM_PARM
|
|
||||||
buffer HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Driver Internal Structures
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
struct VBOX_DEVICE
|
|
||||||
port dw ?
|
|
||||||
pad1 dw ?
|
|
||||||
mmio dd ?
|
|
||||||
mmio_phys dd ?
|
|
||||||
|
|
||||||
ack_virt dd ?
|
|
||||||
ack_phys dd ?
|
|
||||||
display_virt dd ?
|
|
||||||
display_phys dd ?
|
|
||||||
|
|
||||||
hgcm_timeout dd ?
|
|
||||||
|
|
||||||
hgcm_connect_virt dd ?
|
|
||||||
hgcm_connect_phys dd ?
|
|
||||||
hgcm_disconnect_virt dd ?
|
|
||||||
hgcm_disconnect_phys dd ?
|
|
||||||
hgcm_call_virt dd ?
|
|
||||||
hgcm_call_phys dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct CLIPBOARD_STATE
|
|
||||||
client_id dd ?
|
|
||||||
connected dd ?
|
|
||||||
state dd ? ; FSM: 0=INIT, 1=CONNECTING, 2=READY
|
|
||||||
; pending dd ? ; Флаг pending event для worker
|
|
||||||
|
|
||||||
formats_host dd ?
|
|
||||||
formats_guest dd ?
|
|
||||||
|
|
||||||
buffer_virt dd ? ; Основной буфер (UTF-8/raw)
|
|
||||||
buffer_phys dd ?
|
|
||||||
buffer_size dd ?
|
|
||||||
|
|
||||||
utf16_buffer dd ? ; Временный буфер для UTF-16LE конвертации
|
|
||||||
utf16_size dd ?
|
|
||||||
|
|
||||||
last_data_size dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct DISPLAY
|
|
||||||
x dd ?
|
|
||||||
y dd ?
|
|
||||||
width dd ?
|
|
||||||
height dd ?
|
|
||||||
bits_per_pixel dd ?
|
|
||||||
vrefresh dd ?
|
|
||||||
pitch dd ?
|
|
||||||
lfb dd ?
|
|
||||||
ends
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; VirtualBox SharedFolder Structures (ALIGNED)
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
struct SF_STATE
|
|
||||||
connected dd ?
|
|
||||||
client_id dd ?
|
|
||||||
folder_count dd ?
|
|
||||||
pad1 dd ?
|
|
||||||
folders rb sizeof.SF_FOLDER * SHFL_MAX_MAPPINGS
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SF_FOLDER
|
|
||||||
active dd ?
|
|
||||||
root_handle dd ?
|
|
||||||
disk_handle dd ?
|
|
||||||
disk_number dd ?
|
|
||||||
name rb 256
|
|
||||||
host_path rb 1024
|
|
||||||
ends
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; HGCM packets for SharedFolder operations (ALIGNED)
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
struct SHFL_QUERY_MAPPINGS
|
|
||||||
header HGCM_CALL
|
|
||||||
flags HGCM_PARM
|
|
||||||
count HGCM_PARM
|
|
||||||
buffer HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_QUERY_MAP_NAME
|
|
||||||
header HGCM_CALL
|
|
||||||
index HGCM_PARM
|
|
||||||
name HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_MAP_FOLDER
|
|
||||||
header HGCM_CALL
|
|
||||||
path HGCM_PARM
|
|
||||||
root HGCM_PARM
|
|
||||||
delimiter HGCM_PARM
|
|
||||||
case_sens HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_UNMAP_FOLDER
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_CREATE
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
path HGCM_PARM
|
|
||||||
parms HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFLCREATEPARMS
|
|
||||||
handle dd ?
|
|
||||||
result dd ?
|
|
||||||
flags dd ?
|
|
||||||
pad1 dd ?
|
|
||||||
info rb 96
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_CLOSE
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
handle HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_READ
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
handle HGCM_PARM
|
|
||||||
offset HGCM_PARM
|
|
||||||
size HGCM_PARM
|
|
||||||
buffer HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_WRITE
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
handle HGCM_PARM
|
|
||||||
offset HGCM_PARM
|
|
||||||
size HGCM_PARM
|
|
||||||
buffer HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_LIST
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
path HGCM_PARM
|
|
||||||
flags HGCM_PARM
|
|
||||||
buffer_size HGCM_PARM
|
|
||||||
buffer HGCM_PARM
|
|
||||||
resume_pt HGCM_PARM
|
|
||||||
file_count HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_INFORMATION
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
handle HGCM_PARM
|
|
||||||
flags HGCM_PARM
|
|
||||||
info HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_REMOVE
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
path HGCM_PARM
|
|
||||||
flags HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_RENAME
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
src HGCM_PARM
|
|
||||||
dst HGCM_PARM
|
|
||||||
flags HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFL_FLUSH
|
|
||||||
header HGCM_CALL
|
|
||||||
root HGCM_PARM
|
|
||||||
handle HGCM_PARM
|
|
||||||
ends
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Helper structures (ALIGNED)
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
struct SHFLSTRING
|
|
||||||
size dw ?
|
|
||||||
length dw ?
|
|
||||||
string rb 4092
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFLFSOBJINFO
|
|
||||||
size dq ?
|
|
||||||
allocated dq ?
|
|
||||||
access_time dq ?
|
|
||||||
modification_time dq ?
|
|
||||||
change_time dq ?
|
|
||||||
birth_time dq ?
|
|
||||||
attr dd ?
|
|
||||||
pad1 dd ?
|
|
||||||
reserved rb 40
|
|
||||||
ends
|
|
||||||
|
|
||||||
struct SHFLMAPPING
|
|
||||||
flags dd ?
|
|
||||||
root dd ?
|
|
||||||
reserved rb 40
|
|
||||||
ends
|
|
||||||
|
|
||||||
; struct SHFLMAPPING
|
|
||||||
; flags dd ?
|
|
||||||
; root dd ?
|
|
||||||
; ends
|
|
||||||
133
common/utils.inc
Normal file
133
common/utils.inc
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; VBoxGuest Driver for KolibriOS - Common Utilities
|
||||||
|
; Файл: common/utils.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; sf_utf8_to_cp866 — Конвертировать UTF-8 строку в CP866 (кириллица)
|
||||||
|
proc sf_utf8_to_cp866 uses ebx ecx edx esi edi, src:dword, dst:dword, src_len:dword
|
||||||
|
mov esi, [src]
|
||||||
|
mov edi, [dst]
|
||||||
|
mov ecx, [src_len]
|
||||||
|
xor edx, edx
|
||||||
|
|
||||||
|
.u8_loop:
|
||||||
|
test ecx, ecx
|
||||||
|
jle .u8_done
|
||||||
|
|
||||||
|
movzx eax, byte [esi]
|
||||||
|
|
||||||
|
cmp al, 0x80
|
||||||
|
jb .u8_ascii
|
||||||
|
|
||||||
|
cmp al, 0xC0
|
||||||
|
jb .u8_skip1
|
||||||
|
cmp al, 0xE0
|
||||||
|
jb .u8_two_byte
|
||||||
|
|
||||||
|
cmp al, 0xF0
|
||||||
|
jb .u8_skip3
|
||||||
|
|
||||||
|
mov byte [edi], '?'
|
||||||
|
inc edi
|
||||||
|
inc edx
|
||||||
|
add esi, 4
|
||||||
|
sub ecx, 4
|
||||||
|
jmp .u8_loop
|
||||||
|
|
||||||
|
.u8_skip3:
|
||||||
|
mov byte [edi], '?'
|
||||||
|
inc edi
|
||||||
|
inc edx
|
||||||
|
add esi, 3
|
||||||
|
sub ecx, 3
|
||||||
|
jmp .u8_loop
|
||||||
|
|
||||||
|
.u8_ascii:
|
||||||
|
mov [edi], al
|
||||||
|
inc esi
|
||||||
|
inc edi
|
||||||
|
inc edx
|
||||||
|
dec ecx
|
||||||
|
jmp .u8_loop
|
||||||
|
|
||||||
|
.u8_two_byte:
|
||||||
|
cmp ecx, 2
|
||||||
|
jb .u8_done
|
||||||
|
|
||||||
|
movzx eax, byte [esi]
|
||||||
|
movzx ebx, byte [esi + 1]
|
||||||
|
and eax, 0x1F
|
||||||
|
shl eax, 6
|
||||||
|
and ebx, 0x3F
|
||||||
|
or eax, ebx
|
||||||
|
|
||||||
|
cmp eax, 0x0401
|
||||||
|
je .u8_yo_upper
|
||||||
|
cmp eax, 0x0451
|
||||||
|
je .u8_yo_lower
|
||||||
|
|
||||||
|
cmp eax, 0x0410
|
||||||
|
jb .u8_not_cyrillic
|
||||||
|
cmp eax, 0x041F
|
||||||
|
jbe .u8_upper1
|
||||||
|
|
||||||
|
cmp eax, 0x042F
|
||||||
|
jbe .u8_upper2
|
||||||
|
|
||||||
|
cmp eax, 0x043F
|
||||||
|
jbe .u8_lower1
|
||||||
|
|
||||||
|
cmp eax, 0x044F
|
||||||
|
jbe .u8_lower2
|
||||||
|
|
||||||
|
jmp .u8_not_cyrillic
|
||||||
|
|
||||||
|
.u8_upper1:
|
||||||
|
sub eax, 0x0410
|
||||||
|
add eax, 0x80
|
||||||
|
jmp .u8_store2
|
||||||
|
|
||||||
|
.u8_upper2:
|
||||||
|
sub eax, 0x0420
|
||||||
|
add eax, 0x90
|
||||||
|
jmp .u8_store2
|
||||||
|
|
||||||
|
.u8_lower1:
|
||||||
|
sub eax, 0x0430
|
||||||
|
add eax, 0xA0
|
||||||
|
jmp .u8_store2
|
||||||
|
|
||||||
|
.u8_lower2:
|
||||||
|
sub eax, 0x0440
|
||||||
|
add eax, 0xE0
|
||||||
|
jmp .u8_store2
|
||||||
|
|
||||||
|
.u8_yo_upper:
|
||||||
|
mov eax, 0xF0
|
||||||
|
jmp .u8_store2
|
||||||
|
|
||||||
|
.u8_yo_lower:
|
||||||
|
mov eax, 0xF1
|
||||||
|
jmp .u8_store2
|
||||||
|
|
||||||
|
.u8_not_cyrillic:
|
||||||
|
mov eax, '?'
|
||||||
|
|
||||||
|
.u8_store2:
|
||||||
|
mov [edi], al
|
||||||
|
add esi, 2
|
||||||
|
sub ecx, 2
|
||||||
|
inc edi
|
||||||
|
inc edx
|
||||||
|
jmp .u8_loop
|
||||||
|
|
||||||
|
.u8_skip1:
|
||||||
|
inc esi
|
||||||
|
dec ecx
|
||||||
|
jmp .u8_loop
|
||||||
|
|
||||||
|
.u8_done:
|
||||||
|
mov byte [edi], 0
|
||||||
|
mov eax, edx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
41
config.inc
Normal file
41
config.inc
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; VBoxGuest Конфигурация
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Автозапуск сервисов
|
||||||
|
; =============================================================================
|
||||||
|
AUTOSTART_MOUSE = 1 ; Абсолютная мыщъх
|
||||||
|
AUTOSTART_HEARTBEAT = 1 ; Heartbeat
|
||||||
|
AUTOSTART_DISPLAY = 1 ; Разрешение экрана
|
||||||
|
AUTOSTART_TIMESYNC = 0 ; Синхронизация времени
|
||||||
|
AUTOSTART_SHARED_FOLDERS = 1 ; Общие папки
|
||||||
|
AUTOSTART_CLIPBOARD = 1 ; Буфер обмена
|
||||||
|
AUTOSTART_GUEST_PROPS = 1 ; Guest Properties
|
||||||
|
AUTOSTART_SEAMLESS = 0 ; Seamless mode
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Настройки сервисов
|
||||||
|
; =============================================================================
|
||||||
|
SHFL_MAX_FOLDERS = 10 ; Максимум общих папок
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Отладка
|
||||||
|
; =============================================================================
|
||||||
|
; Уровни отладки:
|
||||||
|
; __DEBUG_LEVEL__ = 1 -> Полное логирование (очень много вывода)
|
||||||
|
; __DEBUG_LEVEL__ = 2 -> Только ошибки
|
||||||
|
|
||||||
|
__DEBUG__ = 1 ; Включена
|
||||||
|
__DEBUG_LEVEL__ = 2 ;
|
||||||
|
|
||||||
|
__DEBUG_IRQ__ = 0 ; Логирование прерываний
|
||||||
|
__DEBUG_HGCM__ = 0 ; Логирование HGCM
|
||||||
|
__DEBUG_SF__ = 0 ; SharedFolder
|
||||||
|
__DEBUG_CB__ = 0 ; ClipBoard
|
||||||
|
__DEBUG_EVENTS__ = 0 ; Логирование событий
|
||||||
|
__DEBUG_MOUSE__ = 0 ; Логирование мыщъх
|
||||||
|
__DEBUG_SEAMLESS__ = 0
|
||||||
|
__DEBUG_HB__ = 0 ; Heartbeat мониторинг гостя
|
||||||
|
__DEBUG_DISPLAY__ = 0 ; Display разрешение экрана
|
||||||
|
__DEBUG_DISPATCHER__ = 0 ; Логирование диспетчера
|
||||||
13
core/core.inc
Normal file
13
core/core.inc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Core Aggregator
|
||||||
|
; Назначение : Подключение всех модулей ядра драйвера
|
||||||
|
; Файл : core/core.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
include 'core/state.inc'
|
||||||
|
include 'core/pci.inc'
|
||||||
|
include 'core/mmio.inc'
|
||||||
|
include 'core/ports.inc'
|
||||||
|
include 'core/irq.inc'
|
||||||
|
include 'core/timer.inc'
|
||||||
|
include 'core/dispatcher/dispatcher.inc'
|
||||||
345
core/dispatcher/dispatcher.inc
Normal file
345
core/dispatcher/dispatcher.inc
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Service Dispatcher
|
||||||
|
; Файл : core/dispatcher.inc
|
||||||
|
; Назначение : Функции диспетчера сервисов
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
include 'dispatcher_macros.inc'
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Данные диспетчера
|
||||||
|
; =============================================================================
|
||||||
|
align 4
|
||||||
|
dispatcher_active_events dd 0 ; OR всех event_mask включенных сервисов
|
||||||
|
dispatcher_active_caps dd 0 ; OR всех caps_mask включенных сервисов
|
||||||
|
|
||||||
|
|
||||||
|
; dispatcher_find_by_id — Найти сервис по ID
|
||||||
|
;
|
||||||
|
; Вход : svc_id — числовой идентификатор сервиса
|
||||||
|
; Выход: eax = SERVICE_ENTRY* или 0 если не найден
|
||||||
|
proc dispatcher_find_by_id stdcall uses ecx esi, svc_id:dword
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .not_found
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
mov eax, [svc_id]
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
cmp [esi + SERVICE_ENTRY.id], eax
|
||||||
|
je .found
|
||||||
|
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.not_found:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.found:
|
||||||
|
mov eax, esi
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Включить сервис по ID
|
||||||
|
proc dispatcher_enable_by_id stdcall, svc_id:dword
|
||||||
|
stdcall dispatcher_find_by_id, [svc_id]
|
||||||
|
test eax, eax
|
||||||
|
jz .not_found
|
||||||
|
|
||||||
|
stdcall dispatcher_enable_entry, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_found:
|
||||||
|
mov eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Выключить сервис по ID
|
||||||
|
proc dispatcher_disable_by_id stdcall, svc_id:dword
|
||||||
|
stdcall dispatcher_find_by_id, [svc_id]
|
||||||
|
test eax, eax
|
||||||
|
jz .not_found
|
||||||
|
|
||||||
|
stdcall dispatcher_disable_entry, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_found:
|
||||||
|
mov eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Включить сервис по указателю на SERVICE_ENTRY
|
||||||
|
proc dispatcher_enable_entry stdcall uses ebx esi, entry_ptr:dword
|
||||||
|
mov esi, [entry_ptr]
|
||||||
|
|
||||||
|
; Уже включен?
|
||||||
|
cmp dword [esi + SERVICE_ENTRY.enabled], 1
|
||||||
|
je .already_enabled
|
||||||
|
|
||||||
|
; Вызвать fn_enable если есть
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.fn_enable]
|
||||||
|
test eax, eax
|
||||||
|
jz .no_enable_fn
|
||||||
|
|
||||||
|
push esi
|
||||||
|
call eax
|
||||||
|
pop esi
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
jnz .enable_failed
|
||||||
|
|
||||||
|
.no_enable_fn:
|
||||||
|
; Включить
|
||||||
|
mov dword [esi + SERVICE_ENTRY.enabled], 1
|
||||||
|
|
||||||
|
; Добавить маски
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.event_mask]
|
||||||
|
or [dispatcher_active_events], eax
|
||||||
|
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.caps_mask]
|
||||||
|
or [dispatcher_active_caps], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Enabled service ID=%d\n", [esi + SERVICE_ENTRY.id]
|
||||||
|
|
||||||
|
.already_enabled:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.enable_failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Enable failed: 0x%x\n", eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Выключить сервис по указателю
|
||||||
|
proc dispatcher_disable_entry stdcall uses ebx esi, entry_ptr:dword
|
||||||
|
mov esi, [entry_ptr]
|
||||||
|
|
||||||
|
; Уже выключен?
|
||||||
|
cmp dword [esi + SERVICE_ENTRY.enabled], 0
|
||||||
|
je .already_disabled
|
||||||
|
|
||||||
|
; Вызвать fn_disable если есть
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.fn_disable]
|
||||||
|
test eax, eax
|
||||||
|
jz .no_disable_fn
|
||||||
|
|
||||||
|
push esi
|
||||||
|
call eax
|
||||||
|
pop esi
|
||||||
|
|
||||||
|
.no_disable_fn:
|
||||||
|
; Выключить
|
||||||
|
mov dword [esi + SERVICE_ENTRY.enabled], 0
|
||||||
|
|
||||||
|
; Пересчитать активные маски
|
||||||
|
call dispatcher_recalc_masks
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Disabled service ID=%d\n", [esi + SERVICE_ENTRY.id]
|
||||||
|
|
||||||
|
.already_disabled:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; dispatcher_disable_all — Выключить все сервисы
|
||||||
|
proc dispatcher_disable_all uses ecx esi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Disabling all services\n"
|
||||||
|
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
stdcall dispatcher_disable_entry, esi
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Инициализация всех сервисов
|
||||||
|
proc dispatcher_init_all uses ebx ecx esi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Initializing %d services...\n", [services_count]
|
||||||
|
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.fn_init]
|
||||||
|
test eax, eax
|
||||||
|
jz .next
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Init service ID=%d, name=%s\n", [esi + SERVICE_ENTRY.id], [esi + SERVICE_ENTRY.name_ptr]
|
||||||
|
push ecx esi
|
||||||
|
call eax
|
||||||
|
pop esi ecx
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
jnz .init_failed
|
||||||
|
|
||||||
|
.next:
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] All services initialized\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.init_failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Service ID=%d init failed: 0x%x\n", [esi + SERVICE_ENTRY.id], eax
|
||||||
|
jmp .next
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Включить сервисы с autostart=1
|
||||||
|
proc dispatcher_enable_autostart uses ecx esi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Enabling autostart services...\n"
|
||||||
|
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
cmp dword [esi + SERVICE_ENTRY.autostart], 1
|
||||||
|
jne .next
|
||||||
|
|
||||||
|
push ecx esi
|
||||||
|
stdcall dispatcher_enable_entry, esi
|
||||||
|
pop esi ecx
|
||||||
|
|
||||||
|
.next:
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Dispatcher] Autostart done, events=0x%x, caps=0x%x\n", \
|
||||||
|
[dispatcher_active_events], [dispatcher_active_caps]
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Пересчитать активные маски
|
||||||
|
proc dispatcher_recalc_masks uses ebx ecx edx esi
|
||||||
|
xor edx, edx ; events
|
||||||
|
xor ebx, ebx ; caps
|
||||||
|
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
cmp dword [esi + SERVICE_ENTRY.enabled], 1
|
||||||
|
jne .next
|
||||||
|
|
||||||
|
or edx, [esi + SERVICE_ENTRY.event_mask]
|
||||||
|
or ebx, [esi + SERVICE_ENTRY.caps_mask]
|
||||||
|
|
||||||
|
.next:
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
mov [dispatcher_active_events], edx
|
||||||
|
mov [dispatcher_active_caps], ebx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Разослать события включенным сервисам
|
||||||
|
proc dispatcher_dispatch stdcall uses ebx ecx edx esi, event_mask:dword
|
||||||
|
mov edx, [event_mask]
|
||||||
|
test edx, edx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
; Включен?
|
||||||
|
cmp dword [esi + SERVICE_ENTRY.enabled], 0
|
||||||
|
je .next
|
||||||
|
|
||||||
|
; Есть совпадение событий?
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.event_mask]
|
||||||
|
test eax, edx
|
||||||
|
jz .next
|
||||||
|
|
||||||
|
; Есть обработчик?
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.fn_on_event]
|
||||||
|
test eax, eax
|
||||||
|
jz .next
|
||||||
|
|
||||||
|
; Вызвать fn_on_event(event_mask)
|
||||||
|
push ecx edx esi
|
||||||
|
mov eax, edx
|
||||||
|
call [esi + SERVICE_ENTRY.fn_on_event]
|
||||||
|
pop esi edx ecx
|
||||||
|
|
||||||
|
.next:
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Вызвать fn_on_tick у включенных сервисов
|
||||||
|
proc dispatcher_tick_all uses eax ecx esi
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
cmp dword [esi + SERVICE_ENTRY.enabled], 0
|
||||||
|
je .next
|
||||||
|
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.fn_on_tick]
|
||||||
|
test eax, eax
|
||||||
|
jz .next
|
||||||
|
|
||||||
|
push ecx esi
|
||||||
|
call eax
|
||||||
|
pop esi ecx
|
||||||
|
|
||||||
|
.next:
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
; Получить активную маску событий
|
||||||
|
proc dispatcher_get_active_events
|
||||||
|
mov eax, [dispatcher_active_events]
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Получить активную маску capabilities
|
||||||
|
proc dispatcher_get_active_caps
|
||||||
|
mov eax, [dispatcher_active_caps]
|
||||||
|
ret
|
||||||
|
endp
|
||||||
114
core/dispatcher/dispatcher_macros.inc
Normal file
114
core/dispatcher/dispatcher_macros.inc
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Dispatcher Macros
|
||||||
|
; Файл : core/dispatcher_macros.inc
|
||||||
|
; Назначение : Макросы для авторегистрации сервисов VBoxGuest
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Структура SERVICE_ENTRY (52 байта = 13 DWORD)
|
||||||
|
struct SERVICE_ENTRY
|
||||||
|
id dd ? ; +0: Уникальный ID (auto-increment)
|
||||||
|
name_ptr dd ? ; +4: Указатель на строку имени
|
||||||
|
event_mask dd ? ; +8: Маска событий VMMDev
|
||||||
|
caps_mask dd ? ; +12: Маска guest capabilities
|
||||||
|
enabled dd ? ; +16: 0=выключен, 1=включен
|
||||||
|
autostart dd ? ; +20: 1=запуск с драйвером, 0=через vboxctrl
|
||||||
|
fn_init dd ? ; +24: Инициализация (или 0)
|
||||||
|
fn_enable dd ? ; +28: Включение (или 0)
|
||||||
|
fn_disable dd ? ; +32: Выключение (или 0)
|
||||||
|
fn_on_event dd ? ; +36: Обработчик IRQ (или 0)
|
||||||
|
fn_on_tick dd ? ; +40: Обработчик тика (или 0)
|
||||||
|
hgcm_wakeup_event dd ? ; +44: Event handle для пробуждения HGCM-треда из IRQ
|
||||||
|
hgcm_wakeup_event_id dd ? ; +48: Event euid для пробуждения HGCM-треда из IRQ
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Алиас для кода использующего SERVICE_ENTRY_SIZE
|
||||||
|
SERVICE_ENTRY_SIZE = sizeof.SERVICE_ENTRY
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Инициализация списка сервисов (пустой)
|
||||||
|
; =============================================================================
|
||||||
|
__SERVICES_LIST__ equ
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; REGISTER_SERVICE - Регистрация сервиса
|
||||||
|
; ID назначается автоматически (1, 2, 3, ...)
|
||||||
|
; =============================================================================
|
||||||
|
; Это позволяет корректно работать с IRP/forward
|
||||||
|
; =============================================================================
|
||||||
|
macro REGISTER_SERVICE svc_name, event_mask, caps_mask, fn_init, fn_enable, fn_disable, fn_on_event, fn_on_tick, autostart
|
||||||
|
{
|
||||||
|
; Добавляем в список с | как разделителем
|
||||||
|
; Формат: name|evmask|capmask|init|enable|disable|onevent|ontick|autostart
|
||||||
|
match any, __SERVICES_LIST__ \{
|
||||||
|
__SERVICES_LIST__ equ __SERVICES_LIST__,svc_name|event_mask|caps_mask|fn_init|fn_enable|fn_disable|fn_on_event|fn_on_tick|autostart
|
||||||
|
\}
|
||||||
|
match , __SERVICES_LIST__ \{
|
||||||
|
__SERVICES_LIST__ equ svc_name|event_mask|caps_mask|fn_init|fn_enable|fn_disable|fn_on_event|fn_on_tick|autostart
|
||||||
|
\}
|
||||||
|
}
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; BUILD_SERVICE_TABLE - Генерация таблицы сервисов
|
||||||
|
; Создаёт:
|
||||||
|
; services_table - массив SERVICE_ENTRY
|
||||||
|
; services_table_end - метка конца
|
||||||
|
; services_count - количество сервисов (dd)
|
||||||
|
; SVC_ID_xxx - константы ID для каждого сервиса
|
||||||
|
; =============================================================================
|
||||||
|
macro BUILD_SERVICE_TABLE
|
||||||
|
{
|
||||||
|
align 4
|
||||||
|
services_table:
|
||||||
|
|
||||||
|
; Проверка: есть ли сервисы?
|
||||||
|
match , __SERVICES_LIST__ \{
|
||||||
|
display 'WARNING: No services registered!', 13, 10
|
||||||
|
\}
|
||||||
|
|
||||||
|
; Генерация записей
|
||||||
|
match list, __SERVICES_LIST__ \{
|
||||||
|
__BUILD_ENTRIES__ list
|
||||||
|
\}
|
||||||
|
|
||||||
|
services_table_end:
|
||||||
|
services_count dd __BUILD_ID__
|
||||||
|
}
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Вспомогательный макрос для построения записей
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
__BUILD_ID__ = 0
|
||||||
|
|
||||||
|
macro __BUILD_ENTRIES__ [entry]
|
||||||
|
{
|
||||||
|
forward
|
||||||
|
__BUILD_ID__ = __BUILD_ID__ + 1
|
||||||
|
; Парсим entry с разделителем |
|
||||||
|
; Формат: name|evmask|capmask|init|enable|disable|onevent|ontick|autostart
|
||||||
|
match _name|_evmask|_capmask|_init|_enable|_disable|_onevent|_ontick|_auto, entry \{
|
||||||
|
dd __BUILD_ID__ ; id
|
||||||
|
dd _name ; name_ptr
|
||||||
|
dd _evmask ; event_mask
|
||||||
|
dd _capmask ; caps_mask
|
||||||
|
dd 0 ; enabled = 0
|
||||||
|
dd _auto ; autostart
|
||||||
|
dd _init ; fn_init
|
||||||
|
dd _enable ; fn_enable
|
||||||
|
dd _disable ; fn_disable
|
||||||
|
dd _onevent ; fn_on_event
|
||||||
|
dd _ontick ; fn_on_tick
|
||||||
|
dd 0 ; hgcm_wakeup_event (заполняется сервисом в runtime)
|
||||||
|
dd 0 ; hgcm_wakeup_event_id
|
||||||
|
\}
|
||||||
|
}
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Макрос для получения ID по имени переменной (опционально)
|
||||||
|
; Использование: SVC_ID svc_name_var
|
||||||
|
; =============================================================================
|
||||||
|
macro SVC_ID name
|
||||||
|
{
|
||||||
|
; Поиск ID в рантайме через dispatcher_find_by_name
|
||||||
|
; Или использовать константу если известна
|
||||||
|
}
|
||||||
290
core/hgcm.inc
290
core/hgcm.inc
@@ -1,290 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; HGCM Protocol - ИСПРАВЛЕННАЯ ВЕРСИЯ
|
|
||||||
; Исправления:
|
|
||||||
; 1. Правильная инициализация HGCM_CONNECT (очистка буфера имени)
|
|
||||||
; 2. Увеличенный таймаут для async операций
|
|
||||||
; 3. Улучшенная обработка ошибок
|
|
||||||
; 4. Детальное логирование для отладки
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
proc hgcm_wait_async uses ebx ecx esi, request_ptr:dword
|
|
||||||
mov esi, [request_ptr]
|
|
||||||
|
|
||||||
; ИСПРАВЛЕНИЕ #1: Увеличен таймаут в 10 раз для первого подключения
|
|
||||||
; Первое HGCM подключение может занять больше времени
|
|
||||||
mov dword [vbox_device.hgcm_timeout], HGCM_TIMEOUT_DEFAULT * 1000
|
|
||||||
|
|
||||||
DEBUGF 2, "[hgcm] Async wait started, timeout=%d cycles\n", [vbox_device.hgcm_timeout]
|
|
||||||
|
|
||||||
.wait_loop:
|
|
||||||
; CPU Memory Barrier - обязателен перед чтением DMA памяти
|
|
||||||
; Гарантирует что CPU видит последние изменения от устройства
|
|
||||||
mfence
|
|
||||||
|
|
||||||
; Читаем флаги из пакета
|
|
||||||
mov eax, [esi + HGCM_HEADER.flags]
|
|
||||||
DEBUGF 3, "[hgcm] Polling flags: 0x%x\n", eax
|
|
||||||
|
|
||||||
; Проверяем флаг VBOX_HGCM_REQ_DONE (0x1)
|
|
||||||
; Этот флаг устанавливается хостом когда запрос выполнен
|
|
||||||
test eax, VBOX_HGCM_REQ_DONE
|
|
||||||
jnz .completed
|
|
||||||
|
|
||||||
; Уменьшаем счетчик таймаута
|
|
||||||
dec dword [vbox_device.hgcm_timeout]
|
|
||||||
jz .timeout
|
|
||||||
|
|
||||||
; Короткая задержка между проверками
|
|
||||||
; PAUSE инструкция оптимизирует spin-loop на x86
|
|
||||||
mov ecx, 10000
|
|
||||||
.delay_loop:
|
|
||||||
pause
|
|
||||||
loop .delay_loop
|
|
||||||
|
|
||||||
jmp .wait_loop
|
|
||||||
|
|
||||||
.timeout:
|
|
||||||
; КРИТИЧЕСКАЯ ОШИБКА: хост не ответил
|
|
||||||
DEBUGF 1, "[hgcm] *** ASYNC TIMEOUT ***\n"
|
|
||||||
DEBUGF 1, "[hgcm] Current flags: 0x%x\n", [esi + HGCM_HEADER.flags]
|
|
||||||
DEBUGF 1, "[hgcm] Request RC: 0x%x\n", [esi + HGCM_HEADER.header.rc]
|
|
||||||
LOG_DUMP_MEMORY esi, 64
|
|
||||||
mov eax, VERR_TIMEOUT
|
|
||||||
ret
|
|
||||||
|
|
||||||
.completed:
|
|
||||||
; Финальный barrier перед чтением результата
|
|
||||||
mfence
|
|
||||||
DEBUGF 2, "[hgcm] Async completed! flags=0x%x, rc=0x%x, result=0x%x\n", \
|
|
||||||
[esi + HGCM_HEADER.flags], \
|
|
||||||
[esi + HGCM_HEADER.header.rc], \
|
|
||||||
[esi + HGCM_HEADER.result]
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
proc hgcm_send_request uses ebx edx esi edi, request_ptr:dword
|
|
||||||
DEBUGF 2, ">>> hgcm_send_request\n"
|
|
||||||
|
|
||||||
mov esi, [request_ptr]
|
|
||||||
|
|
||||||
; Получаем физический адрес пакета
|
|
||||||
mov eax, esi
|
|
||||||
invoke GetPhysAddr
|
|
||||||
mov edx, eax
|
|
||||||
|
|
||||||
; Проверка что физический адрес валиден
|
|
||||||
test eax, eax
|
|
||||||
jz .phys_addr_failed
|
|
||||||
|
|
||||||
DEBUGF 2, "[hgcm] Request: virt=0x%x, phys=0x%x, size=%d\n", \
|
|
||||||
esi, edx, [esi + HGCM_HEADER.header.size]
|
|
||||||
LOG_DUMP_MEMORY esi, 48
|
|
||||||
|
|
||||||
; Отправляем запрос через VMMDev I/O порт
|
|
||||||
stdcall vmmdev_request_perform, edx
|
|
||||||
|
|
||||||
; Первая проверка: транспортный RC
|
|
||||||
mov eax, [esi + HGCM_HEADER.header.rc]
|
|
||||||
DEBUGF 2, "[hgcm] Transport RC: 0x%x\n", eax
|
|
||||||
|
|
||||||
; Проверяем асинхронное выполнение
|
|
||||||
cmp eax, VINF_HGCM_ASYNC_EXECUTE
|
|
||||||
je .async_request
|
|
||||||
|
|
||||||
; Синхронный запрос - проверяем ошибки транспорта
|
|
||||||
test eax, eax
|
|
||||||
js .transport_error
|
|
||||||
|
|
||||||
jmp .check_service_result
|
|
||||||
|
|
||||||
.async_request:
|
|
||||||
DEBUGF 2, "[hgcm] Async execution, waiting for completion...\n"
|
|
||||||
|
|
||||||
; Ожидаем завершения async операции
|
|
||||||
stdcall hgcm_wait_async, esi
|
|
||||||
test eax, eax
|
|
||||||
jnz .async_wait_failed
|
|
||||||
|
|
||||||
; Проверяем финальный транспортный RC
|
|
||||||
mov eax, [esi + HGCM_HEADER.header.rc]
|
|
||||||
test eax, eax
|
|
||||||
js .transport_error_after_async
|
|
||||||
|
|
||||||
; Продолжаем проверку результата сервиса
|
|
||||||
jmp .check_service_result
|
|
||||||
|
|
||||||
.async_wait_failed:
|
|
||||||
DEBUGF 1, "[hgcm] Async wait failed: 0x%x\n", eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.check_service_result:
|
|
||||||
; Вторая проверка: результат HGCM сервиса
|
|
||||||
mov eax, [esi + HGCM_HEADER.result]
|
|
||||||
DEBUGF 2, "[hgcm] Service result: 0x%x\n", eax
|
|
||||||
|
|
||||||
test eax, eax
|
|
||||||
js .service_error
|
|
||||||
|
|
||||||
; Успех!
|
|
||||||
DEBUGF 2, "[hgcm] Request SUCCESS\n"
|
|
||||||
DEBUGF 2, "<<< hgcm_send_request -> 0\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.phys_addr_failed:
|
|
||||||
DEBUGF 1, "[hgcm] ERROR: GetPhysAddr failed!\n"
|
|
||||||
mov eax, VERR_INVALID_POINTER
|
|
||||||
ret
|
|
||||||
|
|
||||||
.transport_error:
|
|
||||||
push eax
|
|
||||||
stdcall error_get_message, eax
|
|
||||||
mov ebx, eax
|
|
||||||
pop eax
|
|
||||||
DEBUGF 1, "[hgcm] VMMDev transport error: %s (rc=0x%x)\n", ebx, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.transport_error_after_async:
|
|
||||||
push eax
|
|
||||||
stdcall error_get_message, eax
|
|
||||||
mov ebx, eax
|
|
||||||
pop eax
|
|
||||||
DEBUGF 1, "[hgcm] Transport error after async: %s (rc=0x%x)\n", ebx, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.service_error:
|
|
||||||
push eax
|
|
||||||
stdcall error_get_message, eax
|
|
||||||
mov ebx, eax
|
|
||||||
pop eax
|
|
||||||
DEBUGF 1, "[hgcm] HGCM service error: %s (result=0x%x)\n", ebx, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
proc hgcm_connect uses ebx ecx edx esi edi, service_name:dword
|
|
||||||
DEBUGF 2, ">>> hgcm_connect\n"
|
|
||||||
|
|
||||||
mov edi, hgcm_connect_pkt
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; ИСПРАВЛЕНИЕ #2: Правильная инициализация HGCM_CONNECT структуры
|
|
||||||
; =========================================================================
|
|
||||||
|
|
||||||
; 1. Инициализируем VMMDEV заголовок (24 байта)
|
|
||||||
mov dword [edi + HGCM_HEADER.header.size], sizeof.HGCM_CONNECT
|
|
||||||
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CONNECT
|
|
||||||
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved1], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved2], 0
|
|
||||||
|
|
||||||
; 2. Инициализируем HGCM поля (8 байт)
|
|
||||||
mov dword [edi + HGCM_HEADER.flags], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
|
|
||||||
|
|
||||||
; 3. Устанавливаем тип локации
|
|
||||||
; HGCM_LOC_TYPE_PREDEFINED = 2 (встроенный сервис VBox)
|
|
||||||
mov dword [edi + HGCM_CONNECT.location_type], HGCM_LOC_TYPE_PREDEFINED
|
|
||||||
|
|
||||||
; 4. КРИТИЧНО: Очищаем весь буфер service_name
|
|
||||||
; Это исправляет проблему с мусором в неиспользованных байтах
|
|
||||||
push edi
|
|
||||||
lea edi, [edi + HGCM_CONNECT.service_name]
|
|
||||||
xor eax, eax
|
|
||||||
mov ecx, HGCM_SERVICE_NAME_MAX / 4 ; 128/4 = 32 dword
|
|
||||||
rep stosd
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
; 5. Копируем имя сервиса
|
|
||||||
mov esi, [service_name]
|
|
||||||
push edi
|
|
||||||
lea edi, [edi + HGCM_CONNECT.service_name]
|
|
||||||
mov ecx, HGCM_SERVICE_NAME_MAX - 1 ; Оставляем место для \0
|
|
||||||
.copy_name:
|
|
||||||
lodsb
|
|
||||||
test al, al
|
|
||||||
jz .name_done
|
|
||||||
stosb
|
|
||||||
dec ecx
|
|
||||||
jnz .copy_name
|
|
||||||
.name_done:
|
|
||||||
; Гарантируем null-termination
|
|
||||||
xor al, al
|
|
||||||
stosb
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
; 6. Инициализируем client_id = 0 (OUT параметр, будет заполнен хостом)
|
|
||||||
mov dword [edi + HGCM_CONNECT.client_id], 0
|
|
||||||
|
|
||||||
; Логируем пакет перед отправкой
|
|
||||||
push edi
|
|
||||||
lea edi, [edi + HGCM_CONNECT.service_name]
|
|
||||||
DEBUGF 2, "[hgcm] Connecting to service: '%s'\n", edi
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
DEBUGF 2, "[hgcm] Packet size: %d bytes\n", sizeof.HGCM_CONNECT
|
|
||||||
LOG_DUMP_MEMORY edi, sizeof.HGCM_CONNECT
|
|
||||||
|
|
||||||
; 7. Отправляем запрос
|
|
||||||
stdcall hgcm_send_request, edi
|
|
||||||
test eax, eax
|
|
||||||
jnz .error
|
|
||||||
|
|
||||||
; 8. Проверяем что получили client_id != 0
|
|
||||||
mov eax, [edi + HGCM_CONNECT.client_id]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_client_id
|
|
||||||
|
|
||||||
DEBUGF 2, "[hgcm] *** CONNECTED *** Client ID: 0x%x\n", eax
|
|
||||||
DEBUGF 2, "<<< hgcm_connect -> 0x%x\n", eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.no_client_id:
|
|
||||||
DEBUGF 1, "[hgcm] ERROR: client_id = 0 (хост не вернул ID)\n"
|
|
||||||
DEBUGF 1, "[hgcm] Возможные причины:\n"
|
|
||||||
DEBUGF 1, "[hgcm] 1. Сервис не запущен на хосте\n"
|
|
||||||
DEBUGF 1, "[hgcm] 2. Неправильное имя сервиса\n"
|
|
||||||
DEBUGF 1, "[hgcm] 3. VBox Guest Additions отключены\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[hgcm] Connect failed with error: 0x%x\n", eax
|
|
||||||
DEBUGF 2, "<<< hgcm_connect -> 0\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
proc hgcm_disconnect uses edi, client_id:dword
|
|
||||||
DEBUGF 2, ">>> hgcm_disconnect\n"
|
|
||||||
|
|
||||||
mov edi, hgcm_disconnect_pkt
|
|
||||||
|
|
||||||
; Инициализируем заголовок
|
|
||||||
mov dword [edi + HGCM_HEADER.header.size], sizeof.HGCM_DISCONNECT
|
|
||||||
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_DISCONNECT
|
|
||||||
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved1], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved2], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.flags], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
|
|
||||||
|
|
||||||
; Устанавливаем client_id
|
|
||||||
mov eax, [client_id]
|
|
||||||
mov [edi + HGCM_DISCONNECT.client_id], eax
|
|
||||||
|
|
||||||
DEBUGF 2, "[hgcm] Disconnecting client: 0x%x\n", eax
|
|
||||||
|
|
||||||
; Отправляем запрос
|
|
||||||
stdcall hgcm_send_request, edi
|
|
||||||
|
|
||||||
DEBUGF 2, "<<< hgcm_disconnect\n"
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; Глобальные буферы для HGCM пакетов
|
|
||||||
align 4
|
|
||||||
hgcm_connect_pkt: rb sizeof.HGCM_CONNECT
|
|
||||||
hgcm_disconnect_pkt: rb sizeof.HGCM_DISCONNECT
|
|
||||||
534
core/irq.inc
534
core/irq.inc
@@ -1,457 +1,109 @@
|
|||||||
; =============================================================================
|
; =============================================================================
|
||||||
; VirtualBox IRQ Handler
|
; Модуль : IRQ Handler
|
||||||
|
; Назначение : Обработчик прерываний VMMDev
|
||||||
|
; Файл : core/irq.inc
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
|
|
||||||
VMMDEV_MMIO_EVENTS = 0x00
|
|
||||||
VMMDEV_MMIO_EVENT_CLEAR = 0x04
|
|
||||||
|
|
||||||
; Глобальные переменные для IRQ обработки
|
|
||||||
align 4
|
align 4
|
||||||
vbox_pending_events dd 0
|
vbox_irq_count dd 0
|
||||||
vbox_service_running dd 1 ; 1 = running, 0 = should exit
|
|
||||||
vbox_service_event dd 0
|
|
||||||
vbox_service_event_code dd 0
|
|
||||||
|
|
||||||
vbox_service_thread_handle dd ?
|
; vbox_irq_handler — Top-half обработчик IRQ
|
||||||
|
proc vbox_irq_handler stdcall
|
||||||
|
push ebx edx
|
||||||
|
|
||||||
; =============================================================================
|
movzx edx, word [vbox_device.port]
|
||||||
; ПРОЦЕДУРА: vbox_install_irq_handler
|
add dx, VMMDEV_PORT_OFF_REQUEST_FAST
|
||||||
; Установка IRQ обработчика при инициализации драйвера
|
in eax, dx
|
||||||
; =============================================================================
|
|
||||||
proc vbox_install_irq_handler stdcall, irq_line
|
|
||||||
DEBUGF 1, "[irq] Installing IRQ handler for line %d\n", [irq_line]
|
|
||||||
|
|
||||||
; Устанавливаем обработчик прерывания
|
|
||||||
invoke AttachIntHandler, [irq_line], vbox_irq_handler, 0
|
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz .error
|
jz .not_ours
|
||||||
|
|
||||||
DEBUGF 1, "[irq] IRQ handler installed successfully\n"
|
mov ebx, eax
|
||||||
|
|
||||||
|
; ACK все события сразу
|
||||||
|
out dx, eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_IRQ__, "[VBoxGuest] [Dispatch] [IRQ] events=0x%x, service event=0x%x\n", ebx, [dispatcher_active_events]
|
||||||
|
|
||||||
|
; Немедленно разбудить HGCM-треды (Linux: wake_up() из IRQ)
|
||||||
|
test ebx, VMMDEV_EVENT_HGCM
|
||||||
|
jz .no_hgcm_wake
|
||||||
|
call hgcm_irq_dispatch
|
||||||
|
.no_hgcm_wake:
|
||||||
|
|
||||||
|
; Вызвать fn_on_event у всех подходящих сервисов прямо сейчас
|
||||||
|
stdcall dispatcher_dispatch, ebx
|
||||||
|
|
||||||
|
lock inc dword [vbox_irq_count]
|
||||||
|
|
||||||
|
.acked:
|
||||||
|
pop edx ebx
|
||||||
|
mov eax, 1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_ours:
|
||||||
|
pop edx ebx
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; hgcm_irq_dispatch — Разбудить все HGCM-треды из IRQ контекста
|
||||||
|
proc hgcm_irq_dispatch
|
||||||
|
push eax ebx ecx edx esi edi
|
||||||
|
|
||||||
|
mov ecx, [services_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov edi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
cmp dword [edi + SERVICE_ENTRY.enabled], 0
|
||||||
|
je .next
|
||||||
|
|
||||||
|
cmp dword [edi + SERVICE_ENTRY.hgcm_wakeup_event], 0
|
||||||
|
je .next
|
||||||
|
|
||||||
|
mov eax, [edi + SERVICE_ENTRY.hgcm_wakeup_event]
|
||||||
|
mov ebx, [edi + SERVICE_ENTRY.hgcm_wakeup_event_id]
|
||||||
|
xor edx, edx
|
||||||
|
xor esi, esi
|
||||||
|
invoke RaiseEvent
|
||||||
|
|
||||||
|
.next:
|
||||||
|
add edi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
pop edi esi edx ecx ebx eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; vmmdev_irq_install — Установить обработчик IRQ
|
||||||
|
proc vmmdev_irq_install
|
||||||
|
mov eax, [vbox_device.irq]
|
||||||
|
test eax, eax
|
||||||
|
jz .no_irq
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [IRQ] Attaching to IRQ %d\n", eax
|
||||||
|
|
||||||
|
invoke AttachIntHandler, eax, vbox_irq_handler, 0
|
||||||
|
test eax, eax
|
||||||
|
jz .failed
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [IRQ] Handler attached successfully\n"
|
||||||
xor eax, eax
|
xor eax, eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.error:
|
.no_irq:
|
||||||
DEBUGF 2, "[irq] ERROR: Failed to install IRQ handler, eax=0x%x\n", eax
|
DEBUGF 2, "[VBoxGuest] [IRQ] No IRQ line\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
|
||||||
|
.failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [IRQ] Attach failed\n"
|
||||||
mov eax, VERR_INTERNAL_ERROR
|
mov eax, VERR_INTERNAL_ERROR
|
||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_irq_handler
|
|
||||||
; Top half IRQ обработчика - минимум работы в IRQ контексте
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_irq_handler
|
|
||||||
push ebx esi edi
|
|
||||||
|
|
||||||
; 1. Проверяем, наш ли это IRQ
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .not_ours
|
|
||||||
|
|
||||||
; 2. Читаем флаги событий из MMIO
|
|
||||||
mov eax, [eax + VMMDEV_MEMORY.event_flags]
|
|
||||||
test eax, eax
|
|
||||||
jz .not_ours
|
|
||||||
|
|
||||||
DEBUGF 2, "[irq] IRQ received, flags=0x%x\n", eax
|
|
||||||
|
|
||||||
mov ebx, eax
|
|
||||||
|
|
||||||
; 3. Фильтруем интересующие нас события
|
|
||||||
and eax, VMMDEV_EVENT_DISPLAY_CHANGE or VMMDEV_EVENT_HGCM or VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
|
||||||
; test eax, eax
|
|
||||||
; jz .no_relevant_events
|
|
||||||
|
|
||||||
; 4. Атомарно добавляем события в pending
|
|
||||||
lock or [vbox_pending_events], eax
|
|
||||||
|
|
||||||
; 5. ACK события (безопасно для IRQ контекста)
|
|
||||||
; push eax
|
|
||||||
; stdcall vmmdev_ack_events, eax
|
|
||||||
; pop eax
|
|
||||||
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
mov [eax + VMMDEV_MEMORY.event_ack], ebx
|
|
||||||
; mov [edx + 0x18], ebx ; ACK ALL EVENTS
|
|
||||||
|
|
||||||
|
|
||||||
DEBUGF 2, "[irq] Acknowledged events: 0x%x\n", eax
|
|
||||||
|
|
||||||
; ; 6. Если есть HGCM событие (clipboard), будим service thread
|
|
||||||
; test eax, VMMDEV_EVENT_HGCM
|
|
||||||
; jz .no_hgcm_wakeup
|
|
||||||
|
|
||||||
; ; Проверяем, создан ли service thread
|
|
||||||
; cmp dword [vbox_service_event], 0
|
|
||||||
; je .no_hgcm_wakeup
|
|
||||||
|
|
||||||
; ; Будим service thread через event
|
|
||||||
; mov eax, [vbox_service_event]
|
|
||||||
; mov ebx, [vbox_service_event_code]
|
|
||||||
; invoke RaiseEvent
|
|
||||||
|
|
||||||
DEBUGF 2, "[irq] Woke up service thread for HGCM\n"
|
|
||||||
|
|
||||||
; .no_hgcm_wakeup:
|
|
||||||
|
|
||||||
; ; 7. Для display change можно обработать быстрее в IRQ
|
|
||||||
; ; Быстрая обработка display change в IRQ контексте
|
|
||||||
; test eax, VMMDEV_EVENT_DISPLAY_CHANGE
|
|
||||||
; jz .no_display_irq
|
|
||||||
|
|
||||||
; ; Display change требует немедленной реакции
|
|
||||||
; ; Устанавливаем флаг для быстрой обработки
|
|
||||||
; DEBUGF 2, "[irq] Display change detected, scheduling immediate update\n"
|
|
||||||
|
|
||||||
; ; Можно также сразу обработать display change,
|
|
||||||
; ; если операция быстрая и безопасная в IRQ контексте
|
|
||||||
; push eax
|
|
||||||
; call display_change_irq ; быстрая версия display_change
|
|
||||||
; pop eax
|
|
||||||
|
|
||||||
; .no_display_irq:
|
|
||||||
|
|
||||||
; ; 8. Если есть mouse events, также можно обработать быстро
|
|
||||||
; test eax, VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
|
||||||
; jz .no_mouse_irq
|
|
||||||
|
|
||||||
; ; Быстрая обработка мыши в IRQ контексте
|
|
||||||
; DEBUGF 2, "[irq] Mouse position changed\n"
|
|
||||||
|
|
||||||
; ; Здесь можно обновить кэшированные координаты мыши
|
|
||||||
; ; или установить флаг для обработки в service thread
|
|
||||||
|
|
||||||
; .no_mouse_irq:
|
|
||||||
|
|
||||||
; pop edi esi ebx
|
|
||||||
; mov eax, 1 ; IRQ обработан
|
|
||||||
; ret
|
|
||||||
|
|
||||||
; .no_relevant_events:
|
|
||||||
; DEBUGF 2, "[irq] No relevant events (filtered)\n"
|
|
||||||
|
|
||||||
; ; Все равно ACK события, чтобы не залипали
|
|
||||||
; stdcall vmmdev_ack_events, ebx
|
|
||||||
|
|
||||||
pop edi esi ebx
|
|
||||||
mov eax, 1 ; IRQ обработан
|
|
||||||
ret
|
|
||||||
|
|
||||||
.not_ours:
|
|
||||||
pop edi esi ebx
|
|
||||||
xor eax, eax ; не наш IRQ
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: display_change_irq
|
|
||||||
; Быстрая обработка изменения дисплея в IRQ контексте
|
|
||||||
; =============================================================================
|
|
||||||
proc display_change_irq
|
|
||||||
; Минимальная обработка в IRQ контексте
|
|
||||||
; Основная обработка будет в service thread
|
|
||||||
|
|
||||||
; Просто устанавливаем флаг для полной обработки
|
|
||||||
; или сразу читаем новое разрешение
|
|
||||||
|
|
||||||
push eax ebx
|
|
||||||
|
|
||||||
; Быстрое чтение нового разрешения из MMIO
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_mmio
|
|
||||||
|
|
||||||
; Здесь можно прочитать новые параметры дисплея
|
|
||||||
; но основную работу оставляем для service thread
|
|
||||||
|
|
||||||
.no_mmio:
|
|
||||||
pop ebx eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_uninstall_irq_handler
|
|
||||||
; Удаление IRQ обработчика при выгрузке драйвера
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_uninstall_irq_handler
|
|
||||||
DEBUGF 2, "[irq] Uninstalling IRQ handler\n"
|
|
||||||
|
|
||||||
; В KolibriOS нет стандартного способа удалить обработчик
|
|
||||||
; Просто отмечаем, что обработчик больше не активен
|
|
||||||
|
|
||||||
mov dword [vbox_service_running], 0
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_service_init
|
|
||||||
; Инициализация service thread
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_service_init
|
|
||||||
DEBUGF 2, "[service] Initializing service thread\n"
|
|
||||||
|
|
||||||
; 1. Создаём event для синхронизации
|
|
||||||
xor esi, esi
|
|
||||||
xor ecx, ecx
|
|
||||||
invoke CreateEvent
|
|
||||||
test eax, eax
|
|
||||||
jz .event_create_failed
|
|
||||||
|
|
||||||
mov [vbox_service_event], eax
|
|
||||||
mov [vbox_service_event_code], edx
|
|
||||||
|
|
||||||
DEBUGF 2, "[service] Event created: handle=0x%x, code=0x%x\n", eax, edx
|
|
||||||
|
|
||||||
; 2. Создаём service thread
|
|
||||||
push ebx ecx edx esi edi
|
|
||||||
movi ebx, 1 ; priority = 1 (низкий)
|
|
||||||
mov ecx, vbox_service_thread ; entry point
|
|
||||||
xor edx, edx ; parameter = NULL
|
|
||||||
invoke CreateThread
|
|
||||||
pop edi esi edx ecx ebx
|
|
||||||
|
|
||||||
cmp eax, -1
|
|
||||||
je .thread_create_failed
|
|
||||||
|
|
||||||
mov [vbox_service_thread_handle], eax
|
|
||||||
DEBUGF 2, "[service] Service thread created, handle=0x%x\n", eax
|
|
||||||
|
|
||||||
; 3. Устанавливаем флаг запуска
|
|
||||||
mov dword [vbox_service_running], 1
|
|
||||||
mov dword [vbox_pending_events], 0
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.event_create_failed:
|
|
||||||
DEBUGF 1, "[service] ERROR: Failed to create event\n"
|
|
||||||
or eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.thread_create_failed:
|
|
||||||
DEBUGF 1, "[service] ERROR: Failed to create service thread\n"
|
|
||||||
|
|
||||||
; Освобождаем event
|
|
||||||
mov eax, [vbox_service_event]
|
|
||||||
test eax, eax
|
|
||||||
jz @f
|
|
||||||
invoke DestroyEvent
|
|
||||||
@@:
|
|
||||||
mov dword [vbox_service_event], 0
|
|
||||||
or eax, -1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_service_cleanup
|
|
||||||
; Очистка ресурсов service thread
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_service_cleanup
|
|
||||||
DEBUGF 2, "[service] Cleaning up service thread\n"
|
|
||||||
|
|
||||||
; 1. Устанавливаем флаг завершения
|
|
||||||
mov dword [vbox_service_running], 0
|
|
||||||
|
|
||||||
; 2. Будим thread для завершения (если спит)
|
|
||||||
mov eax, [vbox_service_event]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_event
|
|
||||||
mov ebx, [vbox_service_event_code]
|
|
||||||
invoke RaiseEvent
|
|
||||||
|
|
||||||
; 3. Ждем завершения thread (краткий таймаут)
|
|
||||||
push esi
|
|
||||||
movi esi, 50 ; 50ms timeout
|
|
||||||
invoke Sleep
|
|
||||||
pop esi
|
|
||||||
|
|
||||||
.no_event:
|
|
||||||
; 4. Освобождаем event
|
|
||||||
mov eax, [vbox_service_event]
|
|
||||||
test eax, eax
|
|
||||||
jz @f
|
|
||||||
invoke DestroyEvent
|
|
||||||
@@:
|
|
||||||
mov dword [vbox_service_event], 0
|
|
||||||
mov dword [vbox_service_event_code], 0
|
|
||||||
mov dword [vbox_service_thread_handle], 0
|
|
||||||
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_service_thread
|
|
||||||
; Основной service thread (bottom half обработки)
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_service_thread
|
|
||||||
DEBUGF 2, "[service] Service thread started\n"
|
|
||||||
|
|
||||||
.service_loop:
|
|
||||||
; 1. Ждем события или timeout
|
|
||||||
mov eax, [vbox_service_event]
|
|
||||||
mov ebx, [vbox_service_event_code]
|
|
||||||
; invoke WaitEventTimeout, eax, ebx, 1000 ; timeout 1000ms
|
|
||||||
invoke WaitEvent, eax, ebx
|
|
||||||
; 2. Проверяем, не пора ли завершать thread
|
|
||||||
cmp dword [vbox_service_running], 0
|
|
||||||
je .exit_thread
|
|
||||||
|
|
||||||
; 3. Обрабатываем pending events
|
|
||||||
call vbox_process_pending_events
|
|
||||||
|
|
||||||
; 4. Продолжаем цикл
|
|
||||||
jmp .service_loop
|
|
||||||
|
|
||||||
.exit_thread:
|
|
||||||
DEBUGF 2, "[service] Service thread exiting\n"
|
|
||||||
or eax, -1
|
|
||||||
int 0x40
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_process_pending_events
|
|
||||||
; Обработка всех pending events
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_process_pending_events
|
|
||||||
push ebx esi edi
|
|
||||||
|
|
||||||
.process_loop:
|
|
||||||
; 1. Атомарно читаем и сбрасываем pending events
|
|
||||||
cli
|
|
||||||
mov eax, [vbox_pending_events]
|
|
||||||
mov dword [vbox_pending_events], 0
|
|
||||||
sti
|
|
||||||
|
|
||||||
; 2. Если нет событий - выходим
|
|
||||||
test eax, eax
|
|
||||||
jz .done
|
|
||||||
|
|
||||||
DEBUGF 2, "[service] Processing events: 0x%x\n", eax
|
|
||||||
|
|
||||||
; 3. Сохраняем события для обработки
|
|
||||||
push eax
|
|
||||||
|
|
||||||
; =====================================================================
|
|
||||||
; Обработка DISPLAY_CHANGE (полная версия)
|
|
||||||
; =====================================================================
|
|
||||||
test eax, VMMDEV_EVENT_DISPLAY_CHANGE
|
|
||||||
jz .no_display_change
|
|
||||||
|
|
||||||
DEBUGF 2, "[service] Handling display change (full)\n"
|
|
||||||
call display_change
|
|
||||||
|
|
||||||
.no_display_change:
|
|
||||||
|
|
||||||
; =====================================================================
|
|
||||||
; Обработка HGCM (clipboard)
|
|
||||||
; =====================================================================
|
|
||||||
test eax, VMMDEV_EVENT_HGCM
|
|
||||||
jz .no_hgcm_event
|
|
||||||
|
|
||||||
; Проверяем, что clipboard инициализирован
|
|
||||||
cmp dword [clipboard_state.connected], 1
|
|
||||||
jne .hgcm_not_ready
|
|
||||||
|
|
||||||
DEBUGF 2, "[service] Handling HGCM clipboard event\n"
|
|
||||||
|
|
||||||
; Включаем прерывания на время обработки
|
|
||||||
sti
|
|
||||||
|
|
||||||
call clipboard_handle_event
|
|
||||||
|
|
||||||
; Выключаем обратно для атомарных операций
|
|
||||||
cli
|
|
||||||
|
|
||||||
jmp .no_hgcm_event
|
|
||||||
|
|
||||||
.hgcm_not_ready:
|
|
||||||
DEBUGF 2, "[service] HGCM event ignored (clipboard not ready)\n"
|
|
||||||
|
|
||||||
.no_hgcm_event:
|
|
||||||
|
|
||||||
; =====================================================================
|
|
||||||
; Обработка MOUSE events
|
|
||||||
; =====================================================================
|
|
||||||
test eax, VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
|
||||||
jz .no_mouse_event
|
|
||||||
|
|
||||||
DEBUGF 2, "[service] Handling mouse position change\n"
|
|
||||||
call mouse_position_changed
|
|
||||||
|
|
||||||
.no_mouse_event:
|
|
||||||
|
|
||||||
; 4. Восстанавливаем оригинальные события
|
|
||||||
pop eax
|
|
||||||
|
|
||||||
; 5. Проверяем, не появились ли новые события пока мы обрабатывали
|
|
||||||
; (это безопасно, т.к. мы уже обработали текущие)
|
|
||||||
jmp .process_loop
|
|
||||||
|
|
||||||
.done:
|
|
||||||
pop edi esi ebx
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: mouse_position_changed
|
|
||||||
; Обработка изменения позиции мыши
|
|
||||||
; =============================================================================
|
|
||||||
proc mouse_position_changed
|
|
||||||
push eax ebx ecx edx
|
|
||||||
|
|
||||||
; Читаем новые координаты мыши из MMIO
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_mmio
|
|
||||||
|
|
||||||
; Здесь можно обновить состояние мыши в системе
|
|
||||||
|
|
||||||
.no_mmio:
|
|
||||||
pop edx ecx ebx eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_check_events
|
|
||||||
; Ручная проверка событий (для polling режима)
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_check_events
|
|
||||||
; Проверяем pending events вручную
|
|
||||||
mov eax, [vbox_pending_events]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_events
|
|
||||||
|
|
||||||
; Если есть service thread, он обработает
|
|
||||||
cmp dword [vbox_service_thread_handle], 0
|
|
||||||
jne .thread_will_handle
|
|
||||||
|
|
||||||
; Иначе обрабатываем сами
|
|
||||||
call vbox_process_pending_events
|
|
||||||
|
|
||||||
.thread_will_handle:
|
|
||||||
.no_events:
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_clear_pending_events
|
|
||||||
; Очистка pending events
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_clear_pending_events
|
|
||||||
mov dword [vbox_pending_events], 0
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vbox_get_pending_events
|
|
||||||
; Получение текущих pending events
|
|
||||||
; Возврат: EAX = pending events flags
|
|
||||||
; =============================================================================
|
|
||||||
proc vbox_get_pending_events
|
|
||||||
mov eax, [vbox_pending_events]
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|||||||
74
core/mmio.inc
Normal file
74
core/mmio.inc
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Memory Mapped I/O Operations
|
||||||
|
; Назначение : Работа с MMIO областью VMMDev
|
||||||
|
; =============================================================================
|
||||||
|
proc mmio_map_vmmdev
|
||||||
|
; Маппинг физической памяти в виртуальное адресное пространство
|
||||||
|
invoke MapIoMem, [vbox_device.mmio_phys], VMMDEV_MEMORY_SIZE, PG_NOCACHE + PG_SW
|
||||||
|
test eax, eax
|
||||||
|
jz .map_failed
|
||||||
|
|
||||||
|
; Сохраняем виртуальный адрес
|
||||||
|
mov [vbox_device.mmio_virt], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] Mapped to virtual address: 0x%x\n", eax
|
||||||
|
|
||||||
|
; Проверяем версию VMMDev
|
||||||
|
call mmio_check_version
|
||||||
|
test eax, eax
|
||||||
|
jnz .version_check_failed
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] Mapping completed successfully\n"
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.map_failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] ERROR: MapIoMem failed\n"
|
||||||
|
mov eax, VERR_NO_MEMORY
|
||||||
|
ret
|
||||||
|
|
||||||
|
.version_check_failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] ERROR: VMMDev version check failed\n"
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
; mmio_check_version — Проверка версии VMMDev структуры в MMIO
|
||||||
|
proc mmio_check_version
|
||||||
|
mov edi, [vbox_device.mmio_virt]
|
||||||
|
|
||||||
|
; Отладочный вывод структуры VMMDev
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] VMMDev structure at 0x%x:\n", edi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] size: 0x%x\n", [edi + VMMDEV_MEMORY.size]
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] version: 0x%x\n", [edi + VMMDEV_MEMORY.version]
|
||||||
|
|
||||||
|
; Проверяем минимальный размер структуры
|
||||||
|
mov eax, [edi + VMMDEV_MEMORY.size]
|
||||||
|
cmp eax, sizeof.VMMDEV_MEMORY
|
||||||
|
jb .invalid_size
|
||||||
|
|
||||||
|
; Проверяем версию VMMDev
|
||||||
|
mov eax, [edi + VMMDEV_MEMORY.version]
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] Detected VMMDev version: 0x%x (required: 0x%x)\n", \
|
||||||
|
eax, VMMDEV_MEMORY_VERSION
|
||||||
|
|
||||||
|
; Проверяем совместимость версии (должна быть >= требуемой)
|
||||||
|
cmp eax, VMMDEV_MEMORY_VERSION
|
||||||
|
jae .version_ok
|
||||||
|
|
||||||
|
; Устаревшая версия - выводим предупреждение, но продолжаем
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] WARNING: Old VMMDev version: 0x%x (expected >= 0x%x)\n", \
|
||||||
|
eax, VMMDEV_MEMORY_VERSION
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] Some features may be unavailable\n"
|
||||||
|
|
||||||
|
.version_ok:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] VMMDev version check passed\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.invalid_size:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [MMIO] ERROR: Invalid VMMDev structure size: %d (expected >= %d)\n", \
|
||||||
|
eax, sizeof.VMMDEV_MEMORY
|
||||||
|
mov eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
117
core/pci.inc
Normal file
117
core/pci.inc
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : PCI Device Detection
|
||||||
|
; Назначение : Обнаружение и идентификация PCI устройства VirtualBox
|
||||||
|
; Файл : core/pci.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
align 4
|
||||||
|
pci_device dd ? ; Указатель на структуру PCI устройства VBox
|
||||||
|
|
||||||
|
; vmmdev_probe — Поиск PCI устройства VirtualBox в системе
|
||||||
|
proc vmmdev_probe
|
||||||
|
call pci_find_vmmdev
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
call pci_init_vmmdev_irq
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
call pci_init_vmmdev_bar
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
.fail:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] Initialization failed\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
proc pci_find_vmmdev
|
||||||
|
; Получаем список PCI устройств
|
||||||
|
invoke GetPCIList
|
||||||
|
mov ebx, eax ; EBX = начало списка (якорь)
|
||||||
|
|
||||||
|
.search_loop:
|
||||||
|
mov eax, [eax + PCIDEV.fd]
|
||||||
|
cmp eax, ebx
|
||||||
|
je .not_found
|
||||||
|
|
||||||
|
; Сравниваем Vendor/Device ID
|
||||||
|
mov edx, [eax + PCIDEV.vendor_device_id]
|
||||||
|
cmp edx, (VBOX_DEVICE_ID shl 16) + VBOX_VENDOR_ID
|
||||||
|
jne .search_loop
|
||||||
|
|
||||||
|
.found:
|
||||||
|
mov [pci_device], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] VBox device found\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_found:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] VBox device NOT found\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; pci_init_vmmdev_irq — Чтение IRQ линии из PCI конфигурации
|
||||||
|
proc pci_init_vmmdev_irq
|
||||||
|
mov ebx, [pci_device]
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] pci_device=0x%x\n", [pci_device]
|
||||||
|
test ebx, ebx
|
||||||
|
jz .no_device
|
||||||
|
|
||||||
|
; Читаем IRQ линию из PCI конфигурации
|
||||||
|
invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.interrupt_line
|
||||||
|
movzx eax, al ; IRQ линия - младший байт
|
||||||
|
|
||||||
|
; Проверяем валидность IRQ (должна быть 0-15)
|
||||||
|
cmp eax, 16
|
||||||
|
jae .invalid_irq
|
||||||
|
|
||||||
|
; Сохраняем IRQ в структуре устройства
|
||||||
|
mov [vbox_device.irq], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] IRQ line: %d\n", eax
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_device:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] ERROR: No PCI device for IRQ init\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
|
||||||
|
.invalid_irq:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] ERROR: Invalid IRQ line: %d\n", eax
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
|
||||||
|
endp
|
||||||
|
|
||||||
|
; pci_init_vmmdev_bar — Чтение BAR0 (I/O) и BAR1 (MMIO) из PCI конфигурации
|
||||||
|
proc pci_init_vmmdev_bar
|
||||||
|
mov ebx, [pci_device]
|
||||||
|
test ebx, ebx
|
||||||
|
jz .no_device
|
||||||
|
|
||||||
|
; Читаем BAR0 (I/O port)
|
||||||
|
invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.base_addr_0
|
||||||
|
and eax, not 0xF
|
||||||
|
mov [vbox_device.port], ax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] BAR0 (I/O Port): 0x%x\n", eax
|
||||||
|
|
||||||
|
; Читаем BAR1 (MMIO)
|
||||||
|
invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.base_addr_1
|
||||||
|
and eax, not 0xF
|
||||||
|
mov [vbox_device.mmio_phys], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] BAR1 (MMIO): phys=0x%x\n", eax
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_device:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [PCI] ERROR: No PCI device for BAR init\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
|
||||||
|
endp
|
||||||
54
core/ports.inc
Normal file
54
core/ports.inc
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev I/O Ports
|
||||||
|
; Назначение : Операции через I/O порты VMMDev (fast request/events)
|
||||||
|
; Файл : core/ports.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; vmmdev_send_request — отправка физического адреса VMMDev-запроса в I/O порт
|
||||||
|
;
|
||||||
|
; Вход : phys_addr — физ. адрес VMMDev-пакета
|
||||||
|
; Выход: —
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc vmmdev_send_request uses edx, phys_addr:dword
|
||||||
|
mov eax, [phys_addr]
|
||||||
|
|
||||||
|
mov dx, [vbox_device.port]
|
||||||
|
out dx, eax
|
||||||
|
|
||||||
|
; Спин-ожидание (~1ms): даёт хосту время на обработку запроса
|
||||||
|
mov ecx, 1000000
|
||||||
|
.wait:
|
||||||
|
xor eax, eax
|
||||||
|
loop .wait
|
||||||
|
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; ports_init — проверка, что порт задан
|
||||||
|
;
|
||||||
|
; Выход: eax = 0 успех / VERR_INVALID_PARAMETER
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc ports_init
|
||||||
|
movzx eax, word [vbox_device.port]
|
||||||
|
test eax, eax
|
||||||
|
jz .bad
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_PARAMETER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; ports_read_fast_events — прочитать pending mask через FAST порт
|
||||||
|
;
|
||||||
|
; Выход: eax = маска событий
|
||||||
|
proc ports_read_fast_events uses edx
|
||||||
|
mov dx, [vbox_device.port]
|
||||||
|
add dx, VMMDEV_PORT_OFF_REQUEST_FAST
|
||||||
|
in eax, dx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
70
core/state.inc
Normal file
70
core/state.inc
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Driver Internal Structures
|
||||||
|
; Назначение : Глобальная структура состояния драйвера VBoxGuest
|
||||||
|
; Файл : core/state.inc
|
||||||
|
; =============================================================================
|
||||||
|
struct VBOX_DEVICE
|
||||||
|
port dw ?
|
||||||
|
pad1 dw ?
|
||||||
|
mmio_virt dd ?
|
||||||
|
mmio_phys dd ?
|
||||||
|
|
||||||
|
irq dd ?
|
||||||
|
last_events dd ? ; последняя маска событий, прочитанная в IRQ
|
||||||
|
event_filter dd ?
|
||||||
|
caps dd ?
|
||||||
|
|
||||||
|
flags dd ?
|
||||||
|
; бит0: device_present
|
||||||
|
; бит1: mmio_mapped
|
||||||
|
; бит2: irq_attached
|
||||||
|
; бит3: timer_running
|
||||||
|
|
||||||
|
|
||||||
|
; HGCM
|
||||||
|
hgcm_timeout dd ?
|
||||||
|
|
||||||
|
; HGCM packets
|
||||||
|
hgcm_connect_virt dd ?
|
||||||
|
hgcm_connect_phys dd ?
|
||||||
|
hgcm_disconnect_virt dd ?
|
||||||
|
hgcm_disconnect_phys dd ?
|
||||||
|
hgcm_call_virt dd ?
|
||||||
|
hgcm_call_phys dd ?
|
||||||
|
|
||||||
|
; Pre-allocated packets
|
||||||
|
display_virt dd ?
|
||||||
|
display_phys dd ?
|
||||||
|
events_virt dd ?
|
||||||
|
events_phys dd ?
|
||||||
|
filter_virt dd ?
|
||||||
|
filter_phys dd ?
|
||||||
|
caps_virt dd ?
|
||||||
|
caps_phys dd ?
|
||||||
|
guestinfo_virt dd ?
|
||||||
|
guestinfo_phys dd ?
|
||||||
|
guestinfo2_virt dd ?
|
||||||
|
guestinfo2_phys dd ?
|
||||||
|
|
||||||
|
mouse_virt dd ?
|
||||||
|
mouse_phys dd ?
|
||||||
|
|
||||||
|
hypervisor_info_virt dd ?
|
||||||
|
hypervisor_info_phys dd ?
|
||||||
|
dnd_call_virt dd ?
|
||||||
|
dnd_call_phys dd ?
|
||||||
|
host_version_virt dd ?
|
||||||
|
host_version_phys dd ?
|
||||||
|
|
||||||
|
heartbeat_config_virt dd ?
|
||||||
|
heartbeat_config_phys dd ?
|
||||||
|
heartbeat_virt dd ?
|
||||||
|
heartbeat_phys dd ?
|
||||||
|
|
||||||
|
; Pagelist HGCM call buffer (динамически выделяется)
|
||||||
|
hgcm_call_pl_virt dd ?
|
||||||
|
hgcm_call_pl_phys dd ?
|
||||||
|
|
||||||
|
; Единый блок памяти для VMMDev пакетов
|
||||||
|
vmmdev_packets_page dd ?
|
||||||
|
ends
|
||||||
57
core/timer.inc
Normal file
57
core/timer.inc
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Timer
|
||||||
|
; Назначение : Периодический вызов fn_on_tick сервисов
|
||||||
|
; Файл : core/timer.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
align 4
|
||||||
|
vbox_timer_handle dd 0
|
||||||
|
|
||||||
|
TIMER_DELAY_START equ 10 ; 100ms до первого вызова
|
||||||
|
TIMER_INTERVAL equ 10 ; 100ms между вызовами
|
||||||
|
|
||||||
|
; timer_init — Запуск таймера
|
||||||
|
proc timer_init
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Timer] Initializing...\n"
|
||||||
|
|
||||||
|
mov eax, [vbox_timer_handle]
|
||||||
|
test eax, eax
|
||||||
|
jnz .ok
|
||||||
|
|
||||||
|
invoke TimerHS, TIMER_DELAY_START, TIMER_INTERVAL, timer_cb, 0
|
||||||
|
test eax, eax
|
||||||
|
jz .err
|
||||||
|
|
||||||
|
mov [vbox_timer_handle], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Timer] Started, handle=0x%x, interval=%dms\n", \
|
||||||
|
eax, TIMER_INTERVAL * 10
|
||||||
|
|
||||||
|
.ok:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.err:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Timer] ERROR: Failed to start\n"
|
||||||
|
mov eax, VERR_INTERNAL_ERROR
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; timer_stop — Остановка таймера
|
||||||
|
proc timer_stop
|
||||||
|
mov eax, [vbox_timer_handle]
|
||||||
|
test eax, eax
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
invoke CancelTimerHS, eax
|
||||||
|
mov dword [vbox_timer_handle], 0
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Timer] Stopped\n"
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; Callback таймера — вызывает fn_on_tick у всех включенных сервисов
|
||||||
|
proc timer_cb stdcall, userdata:dword
|
||||||
|
stdcall dispatcher_tick_all
|
||||||
|
ret
|
||||||
|
endp
|
||||||
293
core/vmmdev.inc
293
core/vmmdev.inc
@@ -1,293 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; VMMDev Low-level Protocol Implementation
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; Send VMMDev request via I/O port
|
|
||||||
proc vmmdev_request_perform uses ebx ecx edx, phys_addr:dword
|
|
||||||
DEBUGF 2, "vmmdev_request_perform\n"
|
|
||||||
|
|
||||||
mov eax, [phys_addr]
|
|
||||||
DEBUGF 2, "[vmmdev] Sending request at phys=0x%x\n", eax
|
|
||||||
|
|
||||||
vbox_send_pack
|
|
||||||
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; Initialize VMMDev device
|
|
||||||
proc vmmdev_init uses ebx esi edi, pci_dev:dword
|
|
||||||
DEBUGF 2, "vmmdev_init"
|
|
||||||
|
|
||||||
mov ebx, [pci_dev]
|
|
||||||
|
|
||||||
; Get KolibriOS display structure
|
|
||||||
invoke GetDisplay
|
|
||||||
mov [kos_display_ptr], eax
|
|
||||||
|
|
||||||
; Read and attach IRQ
|
|
||||||
invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.interrupt_line
|
|
||||||
movzx eax, al
|
|
||||||
DEBUGF 2, "[vmmdev] IRQ line: %d\n", eax
|
|
||||||
|
|
||||||
stdcall vbox_install_irq_handler, eax
|
|
||||||
jnz .irq_failed
|
|
||||||
|
|
||||||
; Read BAR0 (I/O port) - FIX: use full 32-bit mask
|
|
||||||
invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.base_addr_0
|
|
||||||
and eax, not 0xF
|
|
||||||
mov [vbox_device.port], ax
|
|
||||||
DEBUGF 2, "[vmmdev] I/O Port: 0x%x (%d)\n", eax, eax
|
|
||||||
|
|
||||||
; Read BAR1 (MMIO) - FIX: use full 32-bit mask
|
|
||||||
invoke PciRead32, dword [ebx + PCIDEV.bus], dword [ebx + PCIDEV.devfn], PCI_header00.base_addr_1
|
|
||||||
and eax, not 0xF
|
|
||||||
mov [vbox_device.mmio_phys], eax
|
|
||||||
DEBUGF 2, "[vmmdev] MMIO physical: 0x%x\n", eax
|
|
||||||
|
|
||||||
; Map MMIO region
|
|
||||||
invoke MapIoMem, eax, VMMDEV_MEM_SIZE, PG_NOCACHE + PG_SW
|
|
||||||
test eax, eax
|
|
||||||
jz .mmio_failed
|
|
||||||
|
|
||||||
mov [vbox_device.mmio], eax
|
|
||||||
DEBUGF 2, "[vmmdev] MMIO virtual: 0x%x\n", eax
|
|
||||||
|
|
||||||
; Check VMMDev version
|
|
||||||
mov edi, eax
|
|
||||||
mov eax, [edi + VMMDEV_MEMORY.version]
|
|
||||||
DEBUGF 2, "[vmmdev] VMMDev version: 0x%x\n", eax
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.irq_failed:
|
|
||||||
DEBUGF 1, "[VBoxGuest] Failed to attach IRQ handler\n"
|
|
||||||
mov eax, VERR_GENERAL_FAILURE
|
|
||||||
ret
|
|
||||||
|
|
||||||
.mmio_failed:
|
|
||||||
DEBUGF 1, "[VBoxGuest] Failed to map MMIO region\n"
|
|
||||||
mov eax, VERR_NO_MEMORY
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; Send guest info to host
|
|
||||||
proc vmmdev_send_guest_info
|
|
||||||
DEBUGF 2, "vmmdev_send_guest_info"
|
|
||||||
DEBUGF 2, "[vmmdev] Sending guest info...\n"
|
|
||||||
|
|
||||||
mov eax, vmmdev_guest_info
|
|
||||||
invoke GetPhysAddr
|
|
||||||
stdcall vmmdev_request_perform, eax
|
|
||||||
|
|
||||||
mov eax, [vmmdev_guest_info.header.rc]
|
|
||||||
CHECK_VMMDEV_RC .error
|
|
||||||
|
|
||||||
DEBUGF 2, "[vmmdev] Guest info sent successfully\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
push eax
|
|
||||||
stdcall error_get_message, eax
|
|
||||||
mov esi, eax
|
|
||||||
pop eax
|
|
||||||
DEBUGF 2, "[vmmdev] send_guest_info failed: %s (rc=0x%x)\n", esi, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; Set guest capabilities
|
|
||||||
proc vmmdev_set_guest_caps
|
|
||||||
DEBUGF 2, "[vmmdev] Setting guest capabilities...\n"
|
|
||||||
|
|
||||||
mov eax, vmmdev_guest_caps
|
|
||||||
invoke GetPhysAddr
|
|
||||||
stdcall vmmdev_request_perform, eax
|
|
||||||
|
|
||||||
mov eax, [vmmdev_guest_caps.header.rc]
|
|
||||||
CHECK_VMMDEV_RC .error
|
|
||||||
|
|
||||||
DEBUGF 2, "[vmmdev] Capabilities set\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
push eax
|
|
||||||
stdcall error_get_message, eax
|
|
||||||
mov esi, eax
|
|
||||||
pop eax
|
|
||||||
DEBUGF 2, "[vmmdev] set_guest_caps failed: %s (rc=0x%x)\n", esi, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vmmdev_ack_events
|
|
||||||
; Подтверждение событий в MMIO
|
|
||||||
; Вход: EAX = флаги событий для подтверждения
|
|
||||||
; =============================================================================
|
|
||||||
proc vmmdev_ack_events uses ebx, events:dword
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_mmio
|
|
||||||
|
|
||||||
; Записываем флаги обратно для ACK
|
|
||||||
mov ebx, [events]
|
|
||||||
mov [eax + VMMDEV_MEMORY.event_ack], ebx
|
|
||||||
|
|
||||||
.no_mmio:
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vmmdev_enable_events
|
|
||||||
; Включение генерации событий
|
|
||||||
; Вход: EAX = маска событий для включения
|
|
||||||
; =============================================================================
|
|
||||||
proc vmmdev_enable_events uses ebx, mask:dword
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_mmio
|
|
||||||
|
|
||||||
; Читаем текущие enabled события
|
|
||||||
mov ebx, [eax + VMMDEV_MEMORY.event_enabled]
|
|
||||||
|
|
||||||
; Добавляем новые события
|
|
||||||
or ebx, [mask]
|
|
||||||
|
|
||||||
; Записываем обратно
|
|
||||||
mov [eax + VMMDEV_MEMORY.event_enabled], ebx
|
|
||||||
|
|
||||||
DEBUGF 2, "[vmmdev] Enabled events: 0x%x (total: 0x%x)\n", [mask], ebx
|
|
||||||
|
|
||||||
.no_mmio:
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vmmdev_disable_events
|
|
||||||
; Отключение генерации событий
|
|
||||||
; Вход: EAX = маска событий для отключения
|
|
||||||
; =============================================================================
|
|
||||||
proc vmmdev_disable_events uses ebx, mask:dword
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_mmio
|
|
||||||
|
|
||||||
; Читаем текущие enabled события
|
|
||||||
mov ebx, [eax + VMMDEV_MEMORY.event_enabled]
|
|
||||||
|
|
||||||
; Убираем указанные события
|
|
||||||
mov ecx, [mask]
|
|
||||||
not ecx
|
|
||||||
and ebx, ecx
|
|
||||||
|
|
||||||
; Записываем обратно
|
|
||||||
mov [eax + VMMDEV_MEMORY.event_enabled], ebx
|
|
||||||
|
|
||||||
DEBUGF 2, "[vmmdev] Disabled events: 0x%x (total: 0x%x)\n", [mask], ebx
|
|
||||||
|
|
||||||
.no_mmio:
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: vmmdev_get_pending_events
|
|
||||||
; Получение pending событий (без ACK)
|
|
||||||
; Возврат: EAX = флаги pending событий
|
|
||||||
; =============================================================================
|
|
||||||
proc vmmdev_get_pending_events
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_events
|
|
||||||
|
|
||||||
mov eax, [eax + VMMDEV_MEMORY.event_flags]
|
|
||||||
ret
|
|
||||||
|
|
||||||
.no_events:
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; Enable interrupts
|
|
||||||
proc vmmdev_enable_interrupts
|
|
||||||
DEBUGF 2, "vmmdev_enable_interrupts\n"
|
|
||||||
|
|
||||||
; 1. Получаем указатель на MMIO
|
|
||||||
mov ebx, [vbox_device.mmio]
|
|
||||||
test ebx, ebx
|
|
||||||
jz .no_mmio
|
|
||||||
|
|
||||||
; 2. ACK все pending события
|
|
||||||
mov eax, [ebx + VMMDEV_MEMORY.event_flags]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_pending_events
|
|
||||||
|
|
||||||
; Подтверждаем через MMIO
|
|
||||||
mov [ebx + VMMDEV_MEMORY.event_ack], eax
|
|
||||||
|
|
||||||
; И через VMMDev запрос (чтобы хост знал)
|
|
||||||
push eax
|
|
||||||
mov eax, vmmdev_ack
|
|
||||||
mov [eax + VMMDEV_ACK_EVENTS.events], eax
|
|
||||||
invoke GetPhysAddr
|
|
||||||
stdcall vmmdev_request_perform, eax
|
|
||||||
pop eax
|
|
||||||
|
|
||||||
DEBUGF 2, "[vmmdev] ACKed pending events: 0x%x\n", eax
|
|
||||||
|
|
||||||
.no_pending_events:
|
|
||||||
; 3. Включаем генерацию событий которые нас интересуют
|
|
||||||
mov eax, VMMDEV_EVENT_DISPLAY_CHANGE or VMMDEV_EVENT_HGCM or VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
|
||||||
stdcall vmmdev_enable_events, eax
|
|
||||||
|
|
||||||
; 4. Проверяем что события включились
|
|
||||||
mov eax, [ebx + VMMDEV_MEMORY.event_enabled]
|
|
||||||
DEBUGF 2, "[vmmdev] Event enabled mask: 0x%x\n", eax
|
|
||||||
|
|
||||||
; 5. Также сообщаем хосту что мы готовы получать события
|
|
||||||
; через запрос VMMDEV_REQ_ACKNOWLEDGE_EVENTS
|
|
||||||
mov eax, vmmdev_ack
|
|
||||||
mov dword [eax + VMMDEV_ACK_EVENTS.events], 0xFFFFFFFF ; Подписываемся на все
|
|
||||||
invoke GetPhysAddr
|
|
||||||
stdcall vmmdev_request_perform, eax
|
|
||||||
|
|
||||||
DEBUGF 2, "[vmmdev] Interrupts enabled\n"
|
|
||||||
ret
|
|
||||||
|
|
||||||
.no_mmio:
|
|
||||||
DEBUGF 1, "[vmmdev] ERROR: No MMIO for enable_interrupts\n"
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
|
|
||||||
; Prepared VMMDev packets
|
|
||||||
align 4
|
|
||||||
|
|
||||||
vmmdev_guest_info VMMDEV_GUEST_INFO \
|
|
||||||
<sizeof.VMMDEV_GUEST_INFO, \
|
|
||||||
VMMDEV_REQUEST_HEADER_VERSION, \
|
|
||||||
VMMDEV_REQ_REPORT_GUEST_INFO, \
|
|
||||||
0, 0, 0>, \
|
|
||||||
VMMDEV_VERSION, \
|
|
||||||
VBOXOSTYPE_KOLIBRIOS
|
|
||||||
|
|
||||||
vmmdev_guest_caps VMMDEV_GUEST_CAPS \
|
|
||||||
<sizeof.VMMDEV_GUEST_CAPS, \
|
|
||||||
VMMDEV_REQUEST_HEADER_VERSION, \
|
|
||||||
VMMDEV_REQ_SET_GUEST_CAPS, \
|
|
||||||
0, 0, 0>, \
|
|
||||||
VBOXGUEST_OS_SUPPORTS_CAPS
|
|
||||||
|
|
||||||
vmmdev_ack VMMDEV_ACK_EVENTS \
|
|
||||||
<sizeof.VMMDEV_ACK_EVENTS, \
|
|
||||||
VMMDEV_REQUEST_HEADER_VERSION, \
|
|
||||||
VMMDEV_REQ_ACKNOWLEDGE_EVENTS, \
|
|
||||||
0, 0, 0>, \
|
|
||||||
0
|
|
||||||
|
|
||||||
vmmdev_display VMMDEV_DISPLAY_CHANGE \
|
|
||||||
<sizeof.VMMDEV_DISPLAY_CHANGE, \
|
|
||||||
VMMDEV_REQUEST_HEADER_VERSION, \
|
|
||||||
VMMDEV_REQ_GET_DISPLAY_CHANGE, \
|
|
||||||
0, 0, 0>, \
|
|
||||||
0, 0, 0, 1
|
|
||||||
80
data/clipboard/constants.inc
Normal file
80
data/clipboard/constants.inc
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Clipboard Constants — из VBox 7.2.6 исходников
|
||||||
|
;
|
||||||
|
; Источники:
|
||||||
|
; include/VBox/HostServices/VBoxClipboardSvc.h
|
||||||
|
; include/VBox/GuestHost/SharedClipboard.h
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Guest function numbers (VBOX_SHCL_GUEST_FN_*)
|
||||||
|
; Гость вызывает эти функции через HGCM
|
||||||
|
; =============================================================================
|
||||||
|
VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT equ 1 ; Ждать сообщение от хоста (blocking, deprecated но работает)
|
||||||
|
VBOX_SHCL_GUEST_FN_REPORT_FORMATS equ 2 ; Сообщить хосту о доступных форматах
|
||||||
|
VBOX_SHCL_GUEST_FN_DATA_READ equ 3 ; Прочитать данные с хоста
|
||||||
|
VBOX_SHCL_GUEST_FN_DATA_WRITE equ 4 ; Записать данные на хост
|
||||||
|
VBOX_SHCL_GUEST_FN_CONNECT equ 5 ; (deprecated в 7.x)
|
||||||
|
VBOX_SHCL_GUEST_FN_REPORT_FEATURES equ 6 ; Сообщить о поддерживаемых фичах
|
||||||
|
VBOX_SHCL_GUEST_FN_QUERY_FEATURES equ 7 ; Запросить фичи хоста
|
||||||
|
VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT equ 8 ; Peek без блокировки
|
||||||
|
VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT equ 9 ; Peek с блокировкой
|
||||||
|
VBOX_SHCL_GUEST_FN_MSG_GET equ 10 ; Получить сообщение (новый протокол)
|
||||||
|
VBOX_SHCL_GUEST_FN_MSG_CANCEL equ 26 ; Отменить ожидание
|
||||||
|
|
||||||
|
; Количества параметров
|
||||||
|
VBOX_SHCL_CPARMS_MSG_OLD_GET_WAIT equ 2
|
||||||
|
VBOX_SHCL_CPARMS_REPORT_FORMATS equ 1
|
||||||
|
VBOX_SHCL_CPARMS_DATA_READ equ 3
|
||||||
|
VBOX_SHCL_CPARMS_DATA_WRITE_OLD equ 2 ; Без CONTEXT_ID (старый протокол)
|
||||||
|
VBOX_SHCL_CPARMS_DATA_WRITE equ 3 ; С CONTEXT_ID (новый протокол)
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Host message types (VBOX_SHCL_HOST_MSG_*)
|
||||||
|
; Приходят в parm[0] от MSG_OLD_GET_WAIT
|
||||||
|
;
|
||||||
|
; КРИТИЧНО! В старом коде были НЕПРАВИЛЬНЫЕ значения:
|
||||||
|
; было: FORMATS_REPORT=1, READ_DATA=2, WRITE_DATA=3
|
||||||
|
; надо: QUIT=1, READ_DATA=2, FORMATS_REPORT=3
|
||||||
|
; =============================================================================
|
||||||
|
VBOX_SHCL_HOST_MSG_QUIT equ 1 ; Хост закрывает клипборд
|
||||||
|
VBOX_SHCL_HOST_MSG_READ_DATA equ 2 ; Хост хочет прочитать данные гостя
|
||||||
|
VBOX_SHCL_HOST_MSG_FORMATS_REPORT equ 3 ; Хост сообщает о новых форматах
|
||||||
|
VBOX_SHCL_HOST_MSG_CANCELED equ 4 ; Отмена
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Format flags (VBOX_SHCL_FMT_*) — БИТОВЫЕ МАСКИ
|
||||||
|
;
|
||||||
|
; КРИТИЧНО! В старом коде были НЕПРАВИЛЬНЫЕ значения:
|
||||||
|
; было: FMT_TEXT=1, FMT_UNICODETEXT=13
|
||||||
|
; надо: FMT_UNICODETEXT=1 (bit 0), FMT_BITMAP=2 (bit 1), FMT_HTML=4 (bit 2)
|
||||||
|
; FMT_TEXT не существует в VBox! Только UNICODETEXT (UTF-16LE)
|
||||||
|
; =============================================================================
|
||||||
|
VBOX_SHCL_FMT_NONE equ 0x0000
|
||||||
|
VBOX_SHCL_FMT_UNICODETEXT equ 0x0001 ; bit 0 — UTF-16LE текст
|
||||||
|
VBOX_SHCL_FMT_BITMAP equ 0x0002 ; bit 1 — DIB bitmap
|
||||||
|
VBOX_SHCL_FMT_HTML equ 0x0004 ; bit 2 — HTML
|
||||||
|
VBOX_SHCL_FMT_URI_LIST equ 0x0008 ; bit 3 — URI list (drag-n-drop)
|
||||||
|
VBOX_SHCL_FMT_VALID_MASK equ 0x000F
|
||||||
|
|
||||||
|
; Буфер
|
||||||
|
VBOX_SHCL_MAX_CHUNK_SIZE equ 0x10000 ; 64KB
|
||||||
|
|
||||||
|
; Событие VMMDev для clipboard
|
||||||
|
CLIPBOARD_EVENT_MASK equ VMMDEV_EVENT_HGCM ; bit 1 = 0x02
|
||||||
|
CLIPBOARD_CAPS_MASK equ 0 ; VMMDEV_GUEST_SUPPORTS_SHCL ; bit 7 = 0x80
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Состояния listener'а
|
||||||
|
; =============================================================================
|
||||||
|
CLIP_LISTEN_IDLE equ 0 ; Не слушаем
|
||||||
|
CLIP_LISTEN_SUBMITTED equ 1 ; Запрос отправлен в VMMDev, ждём ответ
|
||||||
|
|
||||||
|
; Максимум ошибок подряд до отключения
|
||||||
|
CLIP_MAX_ERRORS equ 5
|
||||||
|
|
||||||
|
; Флаги KolibriOS события (для CreateEvent)
|
||||||
|
CLIP_MANUAL_DESTROY equ 0x80000000 ; Событие не уничтожается после WaitEvent
|
||||||
|
|
||||||
|
; VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or CLIPBOARD_CAPS_MASK )
|
||||||
|
; VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or CLIPBOARD_EVENT_MASK )
|
||||||
73
data/clipboard/structs.inc
Normal file
73
data/clipboard/structs.inc
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Clipboard (HGCM service: Shared Clipboard / SHCL)
|
||||||
|
; =============================================================================
|
||||||
|
struct SHCL_MSG_GET
|
||||||
|
header HGCM_CALL
|
||||||
|
msg_type HGCM_PARM
|
||||||
|
formats HGCM_PARM
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct SHCL_FORMATS_REPORT
|
||||||
|
header HGCM_CALL
|
||||||
|
formats HGCM_PARM
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct SHCL_DATA_READ
|
||||||
|
header HGCM_CALL
|
||||||
|
format HGCM_PARM
|
||||||
|
buffer HGCM_PARM
|
||||||
|
size HGCM_PARM
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct SHCL_DATA_WRITE
|
||||||
|
header HGCM_CALL
|
||||||
|
format HGCM_PARM
|
||||||
|
buffer HGCM_PARM
|
||||||
|
ends
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Clipboard Structures
|
||||||
|
;
|
||||||
|
; ПРИМЕЧАНИЕ: Старые структуры SHCL_MSG_GET, SHCL_FORMATS_REPORT,
|
||||||
|
; SHCL_DATA_READ, SHCL_DATA_WRITE УДАЛЕНЫ — они не нужны.
|
||||||
|
;
|
||||||
|
; Новый код использует плоский массив HGCM_PARM + hgcm_call32_pagelist,
|
||||||
|
; как в guest_props. Структуры с встроенным HGCM_CALL заголовком
|
||||||
|
; были нужны только для ручного формирования пакетов через
|
||||||
|
; hgcm_send_request, что больше не используется.
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; struct CLIPBOARD_STATE
|
||||||
|
; client_id dd ? ; HGCM client ID
|
||||||
|
; connected dd ? ; 0 = not connected, 1 = connected
|
||||||
|
; formats_host dd ? ; Форматы доступные на хосте (битовая маска VBOX_SHCL_FMT_*)
|
||||||
|
; formats_guest dd ? ; Форматы доступные у гостя
|
||||||
|
; listen_state dd ?
|
||||||
|
; error_count dd ?
|
||||||
|
; ends
|
||||||
|
|
||||||
|
struct CLIPBOARD_STATE
|
||||||
|
client_id dd ? ; HGCM client ID
|
||||||
|
connected dd ? ; 0/1
|
||||||
|
listen_state dd ? ; IDLE/SUBMITTED
|
||||||
|
error_count dd ? ; Счетчик ошибок
|
||||||
|
formats_host dd ? ; Форматы от хоста
|
||||||
|
formats_guest dd ? ; Наши форматы
|
||||||
|
has_data dd ? ; Есть данные для чтения
|
||||||
|
data_size dd ? ; Размер данных
|
||||||
|
|
||||||
|
; Динамически выделяемые буферы
|
||||||
|
data_buf_ptr dd ? ; -> clip_data_buf (KernelAlloc 64K)
|
||||||
|
listen_pkt_virt dd ? ; -> clip_listen_pkt (KernelAlloc 4096, DMA)
|
||||||
|
listen_pkt_phys dd ? ; физ. адрес listen_pkt
|
||||||
|
parms_ptr dd ? ; -> clip_parms (KernelAlloc)
|
||||||
|
debug_buf_ptr dd ? ; -> clip_debug_buf (KernelAlloc)
|
||||||
|
|
||||||
|
; Поток слушателя (thread-based listener)
|
||||||
|
hgcm_event dd ? ; event handle (из CreateEvent)
|
||||||
|
hgcm_event_id dd ? ; event euid (из CreateEvent)
|
||||||
|
thread_id dd ? ; thread ID (из CreateThread)
|
||||||
|
thread_stop dd ? ; 1 = попросить поток завершиться
|
||||||
|
ends
|
||||||
135
data/core/constants.inc
Normal file
135
data/core/constants.inc
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VBoxGuest Core Constants
|
||||||
|
; Файл : data/core/constants.inc
|
||||||
|
; Назначение : VMMDev константы и определения (PCI, версия, маски событий)
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; PCI
|
||||||
|
VBOX_VENDOR_ID equ 0x80EE
|
||||||
|
VBOX_DEVICE_ID equ 0xCAFE
|
||||||
|
|
||||||
|
; VMMDev протокол v1_04
|
||||||
|
VMMDEV_VERSION_MAJOR equ 1
|
||||||
|
VMMDEV_VERSION_MINOR equ 4
|
||||||
|
VMMDEV_VERSION equ (VMMDEV_VERSION_MAJOR shl 16) or VMMDEV_VERSION_MINOR
|
||||||
|
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION equ 0x00010001
|
||||||
|
VMMDEV_HF_FAST_IRQ_ACK equ 0x00000001
|
||||||
|
VMMDEV_MEMORY_VERSION equ 1
|
||||||
|
VMMDEV_MEMORY_SIZE equ 0x00400000
|
||||||
|
|
||||||
|
VMMDEV_PORT_OFF_REQUEST equ 0
|
||||||
|
VMMDEV_PORT_OFF_REQUEST_FAST equ 8
|
||||||
|
|
||||||
|
VMMDEV_MAX_HGCM_PARMS equ 32
|
||||||
|
VMMDEV_MAX_HGCM_DATA_SIZE equ 128 ; MB
|
||||||
|
|
||||||
|
; VMMDev Request Types
|
||||||
|
VMMDEV_REQ_GET_HOST_VERSION equ 4
|
||||||
|
VMMDEV_REQ_ACKNOWLEDGE_EVENTS equ 41
|
||||||
|
VMMDEV_REQ_CTL_GUEST_FILTER_MASK equ 42
|
||||||
|
VMMDEV_REQ_REPORT_GUEST_INFO equ 50
|
||||||
|
VMMDEV_REQ_GET_DISPLAY_CHANGE_2 equ 54
|
||||||
|
VMMDEV_REPORT_GUEST_CAPS equ 55
|
||||||
|
VMMDEV_SET_GUEST_CAPS equ 56
|
||||||
|
VMMDEV_REQ_REPORT_GUEST_INFO2 equ 58
|
||||||
|
VMMDEV_HGCM_CONNECT equ 60
|
||||||
|
VMMDEV_HGCM_DISCONNECT equ 61
|
||||||
|
VMMDEV_HGCM_CALL32 equ 62
|
||||||
|
VMMDEV_HGCM_CALL64 equ 63
|
||||||
|
|
||||||
|
VBOXGSTINFO2_F_REQUESTOR_INFO equ 0x00000001
|
||||||
|
|
||||||
|
; Guest capabilities
|
||||||
|
VMMDEV_GUEST_SUPPORTS_SEAMLESS equ 0x00000001
|
||||||
|
VMMDEV_GUEST_SUPPORTS_GHW_MAPPING equ 0x00000002
|
||||||
|
VMMDEV_GUEST_SUPPORTS_GRAPHICS equ 0x00000004
|
||||||
|
VMMDEV_GUEST_SUPPORTS_VRDP equ 0x00000010
|
||||||
|
VMMDEV_GUEST_SUPPORTS_HGCM equ 0x00000020
|
||||||
|
VMMDEV_GUEST_SUPPORTS_ACPI equ 0x00000040
|
||||||
|
VMMDEV_GUEST_SUPPORTS_SHCL equ 0x00000080
|
||||||
|
VMMDEV_GUEST_SUPPORTS_VRDP_RESIZE equ 0x00000100
|
||||||
|
VMMDEV_GUEST_SUPPORTS_DRAG_AND_DROP equ 0x00000200
|
||||||
|
VMMDEV_GUEST_SUPPORTS_CR3_MONITORING equ 0x00000400
|
||||||
|
VMMDEV_GUEST_SUPPORTS_TIMER_NS equ 0x00000800
|
||||||
|
VMMDEV_GUEST_SUPPORTS_GUEST_HEARTBEAT equ 0x00001000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_HOST_DISPLAY_TOPOLOGY equ 0x00002000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_REQUESTOR equ 0x00004000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_VBVA equ 0x00008000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_SET_GUEST_CAPABILITIES equ 0x00010000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_MOUSE equ 0x00020000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_SHARED_FOLDERS equ 0x00040000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_VIDEO_ACCEL equ 0x00080000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_AUDIO equ 0x00100000
|
||||||
|
VMMDEV_GUEST_SUPPORTS_TSC_EMULATION equ 0x00200000
|
||||||
|
|
||||||
|
; VMMDev события (биты)
|
||||||
|
VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED equ 0x00000001 ; bit 0
|
||||||
|
VMMDEV_EVENT_HGCM equ 0x00000002 ; bit 1
|
||||||
|
VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST equ 0x00000004 ; bit 2
|
||||||
|
VMMDEV_EVENT_JUDGE_CREDENTIALS equ 0x00000008 ; bit 3
|
||||||
|
VMMDEV_EVENT_RESTORED equ 0x00000010 ; bit 4
|
||||||
|
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE equ 0x00000020 ; bit 5
|
||||||
|
VMMDEV_EVENT_BALLOON_CHANGE_REQUEST equ 0x00000040 ; bit 6
|
||||||
|
VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE equ 0x00000080 ; bit 7
|
||||||
|
VMMDEV_EVENT_VRDP equ 0x00000100 ; bit 8
|
||||||
|
VMMDEV_EVENT_MOUSE_POSITION_CHANGED equ 0x00000200 ; bit 9
|
||||||
|
VMMDEV_EVENT_CPU_HOTPLUG equ 0x00000400 ; bit 10
|
||||||
|
VMMDEV_EVENT_VALID_EVENT_MASK equ 0x000007FF ; bits 0-10
|
||||||
|
VMMDEV_EVENT_GUEST_HEARTBEAT equ 0x00040000
|
||||||
|
VMMDEV_EVENT_GUEST_HEARTBEAT_TIMEOUT equ 0x00080000
|
||||||
|
VMMDEV_EVENT_HGCM_ASYNC_CALL equ 0x00100000
|
||||||
|
|
||||||
|
; Накопительные маски событий и capabilities (сервисы добавляют через OR)
|
||||||
|
VBOXGUEST_EVENTS_OR_MASK equ 0
|
||||||
|
VBOXGUEST_EVENTS_NOT_MASK equ 0
|
||||||
|
VBOXGUEST_GUEST_CAPS_OR_MASK equ 0
|
||||||
|
VBOXGUEST_GUEST_CAPS_NOT_MASK equ 0
|
||||||
|
|
||||||
|
; Маска «шумных» событий — НЕ логировать
|
||||||
|
DISPATCHER_NOISY_EVENTS equ (VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED or VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
|
||||||
|
|
||||||
|
; OS Type
|
||||||
|
VBOXOSTYPE_KOLIBRIOS equ 0x00090000
|
||||||
|
|
||||||
|
; HGCM размеры пакетов
|
||||||
|
HGCM_CALL_HEADER_SIZE equ 32
|
||||||
|
HGCM_CALL_BASE_SIZE equ 44
|
||||||
|
HGCM_PARAM_SIZE equ 12
|
||||||
|
|
||||||
|
; Host Features
|
||||||
|
VMMDEV_HVF_MMIO equ 0x00000002
|
||||||
|
|
||||||
|
; Memory Balloon
|
||||||
|
VMMDEV_MEMORY_BALLOON_CHUNK_SIZE equ 0x00100000
|
||||||
|
VMMDEV_MEMORY_BALLOON_CHUNK_PAGES equ 0x100
|
||||||
|
|
||||||
|
; Guest Facility Types
|
||||||
|
VBoxGuestFacilityType_VBoxGuestDriver equ 0
|
||||||
|
VBoxGuestFacilityType_VBoxService equ 1
|
||||||
|
VBoxGuestFacilityType_VBoxTrayClient equ 2
|
||||||
|
VBoxGuestFacilityType_Seamless equ 3
|
||||||
|
VBoxGuestFacilityType_Graphics equ 4
|
||||||
|
|
||||||
|
; Guest Facility Status
|
||||||
|
VBoxGuestFacilityStatus_Inactive equ 0
|
||||||
|
VBoxGuestFacilityStatus_Active equ 1
|
||||||
|
|
||||||
|
; Requestor flags
|
||||||
|
VMMDEV_REQUESTOR_USR_NOT_GIVEN equ 0x00000000
|
||||||
|
VMMDEV_REQUESTOR_USR_DRV equ 0x00000001
|
||||||
|
VMMDEV_REQUESTOR_USR_DRV_OTHER equ 0x00000002
|
||||||
|
VMMDEV_REQUESTOR_USR_ROOT equ 0x00000003
|
||||||
|
VMMDEV_REQUESTOR_USR_USER equ 0x00000006
|
||||||
|
VMMDEV_REQUESTOR_KERNEL equ 0x00000000
|
||||||
|
VMMDEV_REQUESTOR_USERMODE equ 0x00000008
|
||||||
|
VMMDEV_REQUESTOR_CON_DONT_KNOW equ 0x00000000
|
||||||
|
VMMDEV_REQUESTOR_CON_NO equ 0x00000010
|
||||||
|
VMMDEV_REQUESTOR_CON_YES equ 0x00000020
|
||||||
|
VMMDEV_REQUESTOR_GRP_VBOX equ 0x00000080
|
||||||
|
VMMDEV_REQUESTOR_TRUST_NOT_GIVEN equ 0x00000000
|
||||||
|
VMMDEV_REQUESTOR_VBOXGUEST equ (VMMDEV_REQUESTOR_USR_DRV or VMMDEV_REQUESTOR_GRP_VBOX)
|
||||||
|
|
||||||
|
; MMIO смещения
|
||||||
|
VMMDEV_MEMORY_HAVE_EVENTS_V1_04 equ 0x14
|
||||||
|
VMMDEV_MEMORY_HAVE_EVENTS_V1_03 equ 0x0C
|
||||||
297
data/core/structs.inc
Normal file
297
data/core/structs.inc
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; VBoxGuest : Data / VMMDev структуры
|
||||||
|
; Назначение: ABI структуры протокола VMMDev (точное соответствие заголовкам VBox)
|
||||||
|
; Файл : data/core/structs.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Базовый заголовок запросов VMMDev (VMMDevRequestHeader)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_HEADER
|
||||||
|
size dd ? ; 0 - размер пакета
|
||||||
|
version dd ? ; 4 - версия, = VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
request_type dd ? ; 8 - тип запроса
|
||||||
|
rc dd ? ; 12 - код возврата
|
||||||
|
reserved1 dd ? ; 16 - ВАЖНО! Это поле ДОЛЖНО быть, значение 0
|
||||||
|
f_requestor dd ? ; 20 - флаги requestor
|
||||||
|
ends ; Размер = 24 БАЙТА
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Структура VMMDevMemory v1_04
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_MEMORY
|
||||||
|
size dd ? ; 0x00 размер области (в байтах)
|
||||||
|
version dd ? ; 0x04 версия структуры (u32Version)
|
||||||
|
have_events dd ?
|
||||||
|
; vb_va_memory VB_VA_MEMORY
|
||||||
|
ends
|
||||||
|
|
||||||
|
; VBVA memory layout => typedef struct VBVAMEMORY
|
||||||
|
struct VB_VA_MEMORY
|
||||||
|
; fu32_mode_flags dd ?
|
||||||
|
; off32_data dd ?
|
||||||
|
; off32_free dd ?
|
||||||
|
; au8_ring_buffer dd ? ; VMMDEV_VBVA_RING_BUFFER_SIZE
|
||||||
|
; VMMDEVVBVARECORD aRecords[VMMDEV_VBVA_MAX_RECORDS];
|
||||||
|
; index_record_first dd ?
|
||||||
|
; index_record_free dd ?
|
||||||
|
; fu32_supported_orders dd?
|
||||||
|
; reserved1 dd ? ; 0x0C: Зарезервировано
|
||||||
|
; host_features dd ? ; 0x10: Возможности хоста
|
||||||
|
; guest_features dd ? ; 0x14: Возможности гостя
|
||||||
|
; mouse_features dd ? ; 0x18: Возможности мыши
|
||||||
|
; mouse_pos_x dd ? ; 0x1C: Позиция мыши X
|
||||||
|
; mouse_pos_y dd ? ; 0x20: Позиция мыши Y
|
||||||
|
ends
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: Version Request
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_GET_HOST_VERSION
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
major dw ?
|
||||||
|
minor dw ?
|
||||||
|
build dd ?
|
||||||
|
revision dd ?
|
||||||
|
features dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: ReportGuestInfo (VMMDevReq_ReportGuestInfo = 50)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_REPORT_GUEST_INFO
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
interface_version dd ? ; VMMDEV_VERSION (0x00010004)
|
||||||
|
os_type dd ? ; VBOXOSTYPE_*
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: ReportGuestInfo2 (VMMDevReq_ReportGuestInfo2 = 58)
|
||||||
|
; Используется в vgdrvReportGuestInfo
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_GUEST_INFO2
|
||||||
|
additions_major dw ? ; VBOX_VERSION_MAJOR
|
||||||
|
additions_minor dw ? ; VBOX_VERSION_MINOR
|
||||||
|
additions_build dd ? ; VBOX_VERSION_BUILD
|
||||||
|
additions_revision dd ? ; VBOX_SVN_REV
|
||||||
|
additions_features dd ? ; VBOXGSTINFO2_F_REQUESTOR_INFO
|
||||||
|
szName rb 128 ; VBOX_VERSION_STRING
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct VMMDEV_REPORT_GUEST_INFO2
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
guest_info VMMDEV_GUEST_INFO2
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: GetHypervisorInfo (VMMDevReq_GetHypervisorInfo = 2)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_REQ_HYPERVISOR_INFO
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
hypervisorStart dd ? ; GCPhys32
|
||||||
|
hypervisorSize dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: SetHypervisorInfo (VMMDevReq_SetHypervisorInfo = 3)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_SET_HYPERVISOR_INFO
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
hypervisorStart dd ? ; GCPhys32
|
||||||
|
hypervisorSize dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: AcknowledgeEvents (VMMDevReq_AcknowledgeEvents = 41)
|
||||||
|
; В коде: VMMDevEvents
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_EVENTS
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
events dd ? ; IN/OUT: маска событий
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Алиас для совместимости
|
||||||
|
struct VMMDEV_ACKNOWLEDGE_EVENTS
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
events dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: CtlGuestFilterMask (VMMDevReq_CtlGuestFilterMask = 42)
|
||||||
|
; guestEventFilter = (guestEventFilter | u32OrMask) & ~u32NotMask
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_CTL_GUEST_FILTER_MASK
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
or_mask dd ? ; маска OR
|
||||||
|
not_mask dd ? ; маска NOT
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: SetGuestCapabilities (VMMDevReq_SetGuestCapabilities = 55)
|
||||||
|
; guestCaps = (guestCaps | u32OrMask) & ~u32NotMask
|
||||||
|
; VMMDevReqGuestCapabilities2 = VMMDevReq_SetGuestCapabilities
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_SET_GUEST_CAPABILITIES2
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
or_mask dd ? ; маска OR
|
||||||
|
not_mask dd ? ; маска NOT
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Алиас для совместимости
|
||||||
|
struct VMMDEV_GUEST_CAPS2
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
or_mask dd ?
|
||||||
|
not_mask dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: GetDisplayChangeRequest2 (VMMDevReq_GetDisplayChangeRequest2 = 54)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_GET_DISPLAY_CHANGE_REQUEST2
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
x_res dd ?
|
||||||
|
y_res dd ?
|
||||||
|
bpp dd ?
|
||||||
|
event_ack dd ? ; BOOL: 0/1 (false/true)
|
||||||
|
display dd ? ; индекс дисплея (0 для основного)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: ReportGuestStatus (VMMDevReq_ReportGuestStatus = 61)
|
||||||
|
; =============================================================================
|
||||||
|
struct VBOXGUEST_STATUS
|
||||||
|
facility dd ? ; VBoxGuestFacilityType_*
|
||||||
|
status dd ? ; VBoxGuestFacilityStatus_*
|
||||||
|
flags dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct VMMDEV_REPORT_GUEST_STATUS
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
guestStatus VBOXGUEST_STATUS
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Memory Ballooning Structures
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Запрос: GetMemBalloonChangeRequest
|
||||||
|
struct VMMDEV_GET_MEM_BALLOON_CHANGE_REQUEST
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
eventAck dd ?
|
||||||
|
cBalloonChunks dd ?
|
||||||
|
cPhysMemChunks dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Запрос: ChangeMemBalloon
|
||||||
|
struct VMMDEV_CHANGE_MEM_BALLOON
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
cPages dd ?
|
||||||
|
fInflate dd ? ; true = inflate, false = deflate
|
||||||
|
aPhysPage rd VMMDEV_MEMORY_BALLOON_CHUNK_PAGES
|
||||||
|
ends
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Вспомогательные структуры
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; SHFLSTRING - строка с длиной (UTF-8 или UTF-16)
|
||||||
|
struct SHFLSTRING
|
||||||
|
size dw ? ; Размер буфера в байтах
|
||||||
|
length dw ? ; Длина строки в байтах (без null terminator)
|
||||||
|
string rb 0 ; Начало строки (переменный размер)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; SHFLFSOBJINFO - информация о файле/директории (92 байта)
|
||||||
|
; Источник: include/iprt/types.h — RTFSOBJINFO
|
||||||
|
struct SHFLFSOBJINFO
|
||||||
|
size_low dd ? ; +0: cbObject low (int64_t)
|
||||||
|
size_high dd ? ; +4: cbObject high
|
||||||
|
allocated_low dd ? ; +8: cbAllocated low (int64_t)
|
||||||
|
allocated_high dd ? ; +12: cbAllocated high
|
||||||
|
access_time_low dd ? ; +16: AccessTime low (RTTIMESPEC, nanoseconds)
|
||||||
|
access_time_high dd ? ; +20: AccessTime high
|
||||||
|
modification_time_low dd ? ; +24: ModificationTime low
|
||||||
|
modification_time_high dd ? ; +28: ModificationTime high
|
||||||
|
change_time_low dd ? ; +32: ChangeTime low
|
||||||
|
change_time_high dd ? ; +36: ChangeTime high
|
||||||
|
birth_time_low dd ? ; +40: BirthTime low
|
||||||
|
birth_time_high dd ? ; +44: BirthTime high
|
||||||
|
fMode dd ? ; +48: RTFMODE (тип+права: 0x4000=dir, 0x8000=file)
|
||||||
|
attr_reserved rb 40 ; +52: остаток RTFSOBJATTR (enmAdditional + union)
|
||||||
|
ends ; = 48 + 4 + 40 = 92 байт
|
||||||
|
|
||||||
|
; SHFLMAPPING - информация о mapping (для QUERY_MAPPINGS)
|
||||||
|
struct SHFLMAPPING
|
||||||
|
flags dd ? ; Флаги
|
||||||
|
root dd ? ; Root handle
|
||||||
|
; После этого идет SHFLSTRING с именем
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Internal Driver Structures
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
struct VBOXGUEST_WAIT
|
||||||
|
Event dd ? ; RTSEMEVENTMULTI handle
|
||||||
|
ListNode_next dd ? ; RTListNode::pNext
|
||||||
|
ListNode_prev dd ? ; RTListNode::pPrev
|
||||||
|
fReqEvents dd ? ; запрашиваемые события
|
||||||
|
fResEvents dd ? ; полученные события
|
||||||
|
pSession dd ? ; сессия
|
||||||
|
pHGCMReq dd ? ; HGCM запрос (если есть)
|
||||||
|
fPendingWakeUp dd ? ; флаг отложенного пробуждения
|
||||||
|
fFreeMe dd ? ; флаг для освобождения
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct VBOXGUEST_SESSION
|
||||||
|
Process dd ? ; RTPROCESS
|
||||||
|
R0Process dd ? ; RTR0PROCESS
|
||||||
|
pDevExt dd ? ; устройство
|
||||||
|
fRequestor dd ? ; VMMDEV_REQUESTOR_*
|
||||||
|
fUserSession dd ? ; bool
|
||||||
|
SessionSpinlock dd ? ; спинлок для синхронизации сессии
|
||||||
|
ListNode_next dd ? ; RTListNode::pNext
|
||||||
|
ListNode_prev dd ? ; RTListNode::pPrev
|
||||||
|
fEventFilter dd ? ; маска фильтра событий
|
||||||
|
fMouseStatus dd ? ; статус мыши
|
||||||
|
fCapabilities dd ? ; возможности
|
||||||
|
fAcquiredGuestCaps dd ? ; приобретенные возможности
|
||||||
|
aHGCMClientIds rd 32 ; HGCM client IDs
|
||||||
|
fPendingCancelWaitEvents dd ? ; bool
|
||||||
|
u32MousePosChangedSeq dd ? ; последовательность изменения позиции мыши
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Memory Ballooning Internal Structures
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
struct VBOXGUEST_MEMBALLOON
|
||||||
|
hMtx dd ? ; мьютекс
|
||||||
|
cChunks dd ? ; текущее количество чанков
|
||||||
|
cMaxChunks dd ? ; максимальное количество чанков
|
||||||
|
fUseKernelAPI dd ? ; использовать ли API ядра
|
||||||
|
paMemObj dd ? ; указатель на массив объектов памяти
|
||||||
|
pOwner dd ? ; владелец (сессия)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Bit Usage Tracker Structure
|
||||||
|
; =============================================================================
|
||||||
|
struct VBOXGUEST_BITUSAGE_TRACKER
|
||||||
|
acPerBitUsage rd 32 ; счетчики использования битов (0..31)
|
||||||
|
fMask dd ? ; итоговая маска
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct DISPLAY_STATE
|
||||||
|
width dd ?
|
||||||
|
height dd ?
|
||||||
|
bpp dd ?
|
||||||
|
lfb dd ?
|
||||||
|
pitch dd ?
|
||||||
|
refresh_rate dd ?
|
||||||
|
active dd ?
|
||||||
|
pending_change dd ?
|
||||||
|
x dd ?
|
||||||
|
y dd ?
|
||||||
|
ends
|
||||||
26
data/data.inc
Normal file
26
data/data.inc
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; VBoxGuest : Data / VMMDev константы структуры
|
||||||
|
; Назначение: Агрегатор все констант и структур
|
||||||
|
; Файл : data/data.inc
|
||||||
|
; =============================================================================
|
||||||
|
include 'core\constants.inc'
|
||||||
|
include 'core\structs.inc'
|
||||||
|
|
||||||
|
include 'hgcm\constants.inc'
|
||||||
|
include 'hgcm\structs.inc'
|
||||||
|
include 'heartbeat\constants.inc'
|
||||||
|
include 'heartbeat\structs.inc'
|
||||||
|
include 'mouse\constants.inc'
|
||||||
|
include 'mouse\structs.inc'
|
||||||
|
include 'display\constants.inc'
|
||||||
|
include 'display\structs.inc'
|
||||||
|
include 'timesync\constants.inc'
|
||||||
|
include 'timesync\structs.inc'
|
||||||
|
include 'guest_props\constants.inc'
|
||||||
|
include 'guest_props\structs.inc'
|
||||||
|
include 'shared_folders\constants.inc'
|
||||||
|
include 'shared_folders\structs.inc'
|
||||||
|
include 'clipboard\constants.inc'
|
||||||
|
include 'clipboard\structs.inc'
|
||||||
|
include 'seamless\constants.inc'
|
||||||
|
include 'seamless\structs.inc'
|
||||||
33
data/display/constants.inc
Normal file
33
data/display/constants.inc
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Display Service Constants
|
||||||
|
; Файл : data/display/constants.inc
|
||||||
|
; Назначение : Параметры автоизменения разрешения экрана VMMDev Display Change
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Display resolution limits
|
||||||
|
DISP_W_MIN equ 640
|
||||||
|
DISP_H_MIN equ 480
|
||||||
|
DISP_W_MAX equ 3840
|
||||||
|
DISP_H_MAX equ 2160
|
||||||
|
|
||||||
|
; BGA (Bochs Graphics Adapter) register ports
|
||||||
|
VBE_DISPI_IOPORT_INDEX equ 0x01CE
|
||||||
|
VBE_DISPI_IOPORT_DATA equ 0x01CF
|
||||||
|
|
||||||
|
; BGA register indices
|
||||||
|
VBE_DISPI_INDEX_XRES equ 0x01
|
||||||
|
VBE_DISPI_INDEX_YRES equ 0x02
|
||||||
|
VBE_DISPI_INDEX_BPP equ 0x03
|
||||||
|
VBE_DISPI_INDEX_ENABLE equ 0x04
|
||||||
|
|
||||||
|
; BGA enable flags
|
||||||
|
VBE_DISPI_DISABLED equ 0x00
|
||||||
|
VBE_DISPI_ENABLED equ 0x01
|
||||||
|
VBE_DISPI_LFB_ENABLED equ 0x40
|
||||||
|
|
||||||
|
; Event mask for display service
|
||||||
|
DISPLAY_EVENT_MASK equ VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
|
||||||
|
|
||||||
|
; Accumulate global masks
|
||||||
|
VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or VMMDEV_GUEST_SUPPORTS_GRAPHICS )
|
||||||
|
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or DISPLAY_EVENT_MASK )
|
||||||
17
data/display/structs.inc
Normal file
17
data/display/structs.inc
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Display Service Structures
|
||||||
|
; =============================================================================
|
||||||
|
; VMMDEV_GET_DISPLAY_CHANGE_REQUEST2 and DISPLAY_STATE are defined in
|
||||||
|
; data/core/structs.inc
|
||||||
|
|
||||||
|
|
||||||
|
struct DISPLAY
|
||||||
|
x dd ?
|
||||||
|
y dd ?
|
||||||
|
width dd ?
|
||||||
|
height dd ?
|
||||||
|
bits_per_pixel dd ?
|
||||||
|
vrefresh dd ?
|
||||||
|
pitch dd ?
|
||||||
|
lfb dd ?
|
||||||
|
ends
|
||||||
39
data/guest_props/constants.inc
Normal file
39
data/guest_props/constants.inc
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Guest Properties Constants
|
||||||
|
; Файл : data/guest_props/constants.inc
|
||||||
|
; Назначение : HGCM сервис "VBoxGuestPropSvc" функции и константы
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; HGCM function numbers (guest side)
|
||||||
|
GUEST_PROP_FN_GET_PROP equ 1 ; Получить свойство
|
||||||
|
GUEST_PROP_FN_SET_PROP equ 2 ; Установить свойство (с флагами)
|
||||||
|
GUEST_PROP_FN_DEL_PROP equ 4 ; ?? 3 ; Удалить свойство
|
||||||
|
GUEST_PROP_FN_SET_PROP_VALUE equ 3 ; ?? 4 ; Установить значение (без флагов)
|
||||||
|
GUEST_PROP_FN_ENUM_PROPS equ 5 ; Перечислить свойства
|
||||||
|
GUEST_PROP_FN_GET_NOTIFICATION equ 6 ; Ожидать уведомления об изменении
|
||||||
|
|
||||||
|
; GUEST_PROP_FN_HOST_SET_PROP_VALUE equ 4 ; недоступно гостю
|
||||||
|
|
||||||
|
; Максимальные размеры
|
||||||
|
GUEST_PROP_MAX_NAME_LEN equ 256
|
||||||
|
GUEST_PROP_MAX_VALUE_LEN equ 1024
|
||||||
|
GUEST_PROP_MAX_FLAGS_LEN equ 128
|
||||||
|
GUEST_PROP_ENUM_BUF_SIZE equ 4096
|
||||||
|
|
||||||
|
; Количество HGCM параметров для разных операций
|
||||||
|
GUEST_PROP_GET_PARM_COUNT equ 4 ; name, value, timestamp, flags
|
||||||
|
GUEST_PROP_SET_PARM_COUNT equ 3 ; name, value, flags
|
||||||
|
GUEST_PROP_SET_VALUE_PARM_COUNT equ 2 ; name, value
|
||||||
|
GUEST_PROP_DEL_PARM_COUNT equ 1 ; name
|
||||||
|
GUEST_PROP_ENUM_PARM_COUNT equ 3 ; patterns, buffer, size
|
||||||
|
|
||||||
|
; Стандартные свойства для установки при инициализации
|
||||||
|
; /VirtualBox/GuestInfo/OS/Product = "KolibriOS"
|
||||||
|
; /VirtualBox/GuestInfo/OS/Release = "1.0"
|
||||||
|
; /VirtualBox/GuestAdd/VersionExt = "1.0.0"
|
||||||
|
; /VirtualBox/GuestAdd/Revision = "1"
|
||||||
|
|
||||||
|
; Маска событий: Guest Properties не используют VMMDev события напрямую
|
||||||
|
GUEST_PROP_EVENT_MASK equ 0
|
||||||
|
; Capabilities: не требует отдельных guest caps
|
||||||
|
GUEST_PROP_CAPS_MASK equ 0
|
||||||
16
data/guest_props/structs.inc
Normal file
16
data/guest_props/structs.inc
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Guest Properties Structures
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
struct GUEST_PROP_STATE
|
||||||
|
client_id dd ? ; HGCM client ID
|
||||||
|
connected dd ? ; 0/1
|
||||||
|
|
||||||
|
; Динамически выделяемые буферы
|
||||||
|
enum_buf_ptr dd ? ; gp_enum_buf (KernelAlloc 4096)
|
||||||
|
name_buf_ptr dd ? ; gp_name_buf (часть small_bufs)
|
||||||
|
value_buf_ptr dd ? ; gp_value_buf
|
||||||
|
flags_buf_ptr dd ? ; gp_flags_buf
|
||||||
|
parms_ptr dd ? ; gp_parms
|
||||||
|
small_bufs_ptr dd ? ; единый блок (KernelAlloc 4096)
|
||||||
|
ends
|
||||||
11
data/heartbeat/constants.inc
Normal file
11
data/heartbeat/constants.inc
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Heartbeat Service Constants
|
||||||
|
; Файл : data/heartbeat/constants.inc
|
||||||
|
; Назначение : VMMDev heartbeat мониторинг гостя (запросы и события)
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
VMMDEV_REQ_GUEST_HEARTBEAT equ 217 ; VMMDevReq_GuestHeartbeat, Отправка heartbeat
|
||||||
|
VMMDEV_REQ_HEARTBEAT_CONFIGURE equ 218 ; VMMDevReq_HeartbeatConfigure, Настройка heartbeat
|
||||||
|
|
||||||
|
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or VMMDEV_EVENT_GUEST_HEARTBEAT or VMMDEV_EVENT_GUEST_HEARTBEAT_TIMEOUT)
|
||||||
|
VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or VMMDEV_GUEST_SUPPORTS_GUEST_HEARTBEAT )
|
||||||
20
data/heartbeat/structs.inc
Normal file
20
data/heartbeat/structs.inc
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Запрос: HeartbeatConfigure (VMMDevReq_HeartbeatConfigure = 60)
|
||||||
|
; Структура VMMDevReqHeartbeat
|
||||||
|
; cNsInterval - /* OUT: интервал в наносекундах */
|
||||||
|
; f_enabled - флаг включения
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_HEARTBEAT_CONFIGURE
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
c_ns_interval dd ? ; Интервал в наносекундах (младшая часть)
|
||||||
|
c_ns_interval_high dd ? ; Интервал в наносекундах (старшая часть)
|
||||||
|
f_enabled dd ? ; bool(byte) + выравнивание, 1 = включить, 0 = выключить
|
||||||
|
ends ; 24 + 12 байт
|
||||||
|
; AssertCompileSize(VMMDevReqHeartbeat, 24+12);
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Запрос: GuestHeartbeat (VMMDevReq_GuestHeartbeat = 61)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_GUEST_HEARTBEAT
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
ends ; 24 bytes
|
||||||
107
data/hgcm/constants.inc
Normal file
107
data/hgcm/constants.inc
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : HGCM Protocol Constants
|
||||||
|
; Файл : data/hgcm/constants.inc
|
||||||
|
; Назначение : HGCM (Host Guest Communication Manager) протокол константы и параметры
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; HGCM Protocol Constants
|
||||||
|
; =============================================================================
|
||||||
|
VBOX_HGCM_REQ_DONE equ 0x00000001
|
||||||
|
VBOX_HGCM_REQ_CANCELLED equ 0x00000002
|
||||||
|
|
||||||
|
HGCM_SERVICE_NAME_MAX equ 128
|
||||||
|
HGCM_TIMEOUT_ITERS equ 500000 ; Количество итераций внешнего цикла ожидания
|
||||||
|
HGCM_ASYNC_DELAY_ITERS equ 1000 ; Количество pause инструкций во вложенном цикле
|
||||||
|
|
||||||
|
HGCM_LOC_TYPE_PREDEFINED equ 2
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; HGCM Parameter Types (VMMDevHGCMParmType)
|
||||||
|
; Источник: include/VBox/VMMDev.h из исходного кода VirtualBox
|
||||||
|
; =============================================================================
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_INVALID equ 0
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_32BIT equ 1
|
||||||
|
HGCM_PARM_TYPE_32BIT equ VMMDEV_HGCM_PARM_TYPE_32BIT
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_64BIT equ 2
|
||||||
|
; VMMDEV_HGCM_PARM_TYPE_PHYSADDR equ 3 ; устарел
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_LINADDR equ 4 ; In and Out
|
||||||
|
HGCM_PARM_TYPE_LINADDR equ VMMDEV_HGCM_PARM_TYPE_LINADDR
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_LINADDR_IN equ 5 ; Host <- Guest
|
||||||
|
HGCM_PARM_TYPE_LINADDR_IN equ VMMDEV_HGCM_PARM_TYPE_LINADDR_IN
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT equ 6 ; Host -> Guest
|
||||||
|
HGCM_PARM_TYPE_LINADDR_OUT equ VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_LINADDR_LOCKED equ 7 ; Для VBoxGuest, а не хоста
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_LINADDR_LOCKED_IN equ 8 ; Для VBoxGuest, а не хоста
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_LINADDR_LOCKED_OUT equ 9 ; Для VBoxGuest, а не хоста
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_PAGELIST equ 10
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_EMBEDDED equ 11
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_CONTIGUOUS_PAGELIST equ 12
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_NO_BOUNCE_PAGELIST equ 13
|
||||||
|
VMMDEV_HGCM_PARM_TYPE_SIZE_HACK equ 0x7fffffff
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Флаги для EMBEDDED параметров (Embedded.fFlags)
|
||||||
|
; =============================================================================
|
||||||
|
VMMDEV_HGCM_EMBEDDED_FLAG_IN equ 1 ; Данные от гостя к хосту
|
||||||
|
VMMDEV_HGCM_EMBEDDED_FLAG_OUT equ 2 ; Данные от хоста к гостю
|
||||||
|
VMMDEV_HGCM_EMBEDDED_FLAG_BOTH equ 3 ; (IN | OUT)
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Дополнительные константы для проверки возможностей хоста (Host Features)
|
||||||
|
; =============================================================================
|
||||||
|
VMMDEV_HVF_HGCM_EMBEDDED_BUFFERS equ 0x00000002 ; Битовый флаг 1
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; (Опционально) Для обратной совместимости с вашим текущим кодом
|
||||||
|
; =============================================================================
|
||||||
|
; Короткие алиасы для использования в коде:
|
||||||
|
HGCM_PARM_TYPE_64BIT equ VMMDEV_HGCM_PARM_TYPE_64BIT
|
||||||
|
HGCM_PARM_TYPE_LINADDR_INOUT equ VMMDEV_HGCM_PARM_TYPE_LINADDR ; = 4 (bidirectional)
|
||||||
|
HGCM_PARM_TYPE_EMBEDDED equ VMMDEV_HGCM_PARM_TYPE_EMBEDDED
|
||||||
|
HGCM_EMBED_FLAG_IN equ VMMDEV_HGCM_EMBEDDED_FLAG_IN
|
||||||
|
HGCM_EMBED_FLAG_OUT equ VMMDEV_HGCM_EMBEDDED_FLAG_OUT
|
||||||
|
HGCM_EMBED_FLAG_BOTH equ VMMDEV_HGCM_EMBEDDED_FLAG_BOTH
|
||||||
|
|
||||||
|
; Максимальный размер данных для embedded
|
||||||
|
HGCM_MAX_EMBEDDED_DATA equ 4096
|
||||||
|
|
||||||
|
; Максимальный хвост PageList (для pagelist call buffer)
|
||||||
|
HGCM_MAX_PAGELIST_TAIL equ 8192
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Константы PageList
|
||||||
|
; =============================================================================
|
||||||
|
HGCM_PARM_TYPE_PAGELIST equ 10
|
||||||
|
|
||||||
|
; Флаги направления PageList
|
||||||
|
HGCM_PL_FLAG_IN equ 1 ; guest -> host
|
||||||
|
HGCM_PL_FLAG_OUT equ 2 ; host -> guest
|
||||||
|
HGCM_PL_FLAG_BOTH equ 3
|
||||||
|
|
||||||
|
; Размеры страниц
|
||||||
|
PAGE_SIZE equ 4096
|
||||||
|
PAGE_OFFSET_MASK equ 0x0FFF
|
||||||
|
PAGE_BASE_MASK equ 0xFFFFF000
|
||||||
|
|
||||||
|
; Смещения внутри HGCMPageListInfo
|
||||||
|
HGCM_PLI_FLAGS equ 0 ; dd flags
|
||||||
|
HGCM_PLI_OFF_FIRST_PAGE equ 4 ; dw offFirstPage
|
||||||
|
HGCM_PLI_CPAGES equ 6 ; dw cPages
|
||||||
|
HGCM_PLI_APAGES equ 8 ; начало массива dq[] aPages
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Макрос FILL_HGCM_HEADER - Заполнить стандартный заголовок HGCM пакета
|
||||||
|
; Параметры:
|
||||||
|
; reg - регистр с указателем на структуру HGCM_HEADER
|
||||||
|
; size - размер пакета в байтах
|
||||||
|
; =============================================================================
|
||||||
|
macro FILL_HGCM_HEADER reg, size {
|
||||||
|
mov dword [reg + HGCM_HEADER.header.size], size
|
||||||
|
mov dword [reg + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [reg + HGCM_HEADER.header.request_type], VMMDEV_REQ_HGCM_CALL32
|
||||||
|
mov dword [reg + HGCM_HEADER.header.rc], VERR_GENERAL_FAILURE
|
||||||
|
mov dword [reg + HGCM_HEADER.header.reserved1], 0
|
||||||
|
mov dword [reg + HGCM_HEADER.header.requestor], VMMDEV_REQUESTOR_KERNEL
|
||||||
|
}
|
||||||
70
data/hgcm/structs.inc
Normal file
70
data/hgcm/structs.inc
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; VBoxGuest Driver for KolibriOS - Structures HGCM
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; HGCM Structures
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
struct HGCM_HEADER
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
flags dd ?
|
||||||
|
result dd ?
|
||||||
|
ends ; 32 байта
|
||||||
|
|
||||||
|
struct HGCM_CONNECT
|
||||||
|
header HGCM_HEADER
|
||||||
|
location_type dd ?
|
||||||
|
service_name rb HGCM_SERVICE_NAME_MAX ; имя сервиса
|
||||||
|
client_id dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct HGCM_DISCONNECT
|
||||||
|
header HGCM_HEADER
|
||||||
|
client_id dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct HGCM_CALL
|
||||||
|
header HGCM_HEADER ; 32
|
||||||
|
client_id dd ? ; +4 = 36 // ID клиента
|
||||||
|
function dd ? ; +4 = 40 // Номер функции (1=MSG_OLD_GET_WAIT и т.д.)
|
||||||
|
param_count dd ? ; +4 = 44 // Количество параметров
|
||||||
|
; params[0] начинаются с offset 44
|
||||||
|
; Далее идут параметры: struct vmmdev_hgcm_function_parameter params[parm_count];
|
||||||
|
ends
|
||||||
|
|
||||||
|
struct HGCM_PARM
|
||||||
|
type dd ? ; тип параметра (VMMDevHGCMParmType)
|
||||||
|
u rb 8 ; union { uint32_t value32; RTGCPTR pointer; }
|
||||||
|
ends
|
||||||
|
|
||||||
|
HGCM_PARM.u.value32 equ HGCM_PARM.u
|
||||||
|
HGCM_PARM.u.value_or_size equ HGCM_PARM.u
|
||||||
|
|
||||||
|
HGCM_PARM.u.value64_lo equ HGCM_PARM.u
|
||||||
|
HGCM_PARM.u.value64_hi equ HGCM_PARM.u+4
|
||||||
|
|
||||||
|
HGCM_PARM.u.LinAddr.size equ HGCM_PARM.u
|
||||||
|
HGCM_PARM.u.LinAddr.offset equ HGCM_PARM.u+4
|
||||||
|
|
||||||
|
HGCM_PARM.value_or_size equ HGCM_PARM.u
|
||||||
|
HGCM_PARM.offset_or_addr equ HGCM_PARM.u+4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; sizeof(vmmdev_request_header) = 24
|
||||||
|
; sizeof(vmmdev_hgcmreq_header) = 32 // header(24) + flags(4) + result(4)
|
||||||
|
; sizeof(vmmdev_hgcm_call) = 44 // header(32) + client_id(4) + function(4) + parm_count(4)
|
||||||
|
; sizeof(vmmdev_hgcm_function_parameter) = 12
|
||||||
|
; sizeof(vmmdev_hgcm_pagelist) = 16 + (cPages * 8)
|
||||||
|
|
||||||
|
; struct vmmdev_hgcm_pagelist {
|
||||||
|
; u32 flags; // Direction flags (1,2,3)
|
||||||
|
; u16 offFirstPage; // Смещение в первой странице
|
||||||
|
; u16 cPages; // Количество страниц
|
||||||
|
; u64 pages[1]; // Массив физических адресов страниц (RTGCPHYS64)
|
||||||
|
; };
|
||||||
|
|
||||||
|
; Размер буфера для PageList HGCM вызовов
|
||||||
|
; = sizeof.HGCM_CALL(44) + 32*sizeof.HGCM_PARM(12) + 8192 = 8620 → 3 страницы
|
||||||
|
HGCM_PL_BUF_SIZE equ sizeof.HGCM_CALL + (VMMDEV_MAX_HGCM_PARMS * sizeof.HGCM_PARM) + HGCM_MAX_PAGELIST_TAIL
|
||||||
40
data/mouse/constants.inc
Normal file
40
data/mouse/constants.inc
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Mouse Service Constants
|
||||||
|
; Файл : data/mouse/constants.inc
|
||||||
|
; Назначение : VMMDev запросы мыши (абсолютные координаты, кнопки, скролл)
|
||||||
|
; =============================================================================
|
||||||
|
VMMDEV_REQ_GET_MOUSE_STATUS equ 1
|
||||||
|
VMMDEV_REQ_SET_MOUSE_STATUS equ 2
|
||||||
|
VMMDEV_REQ_SET_POINTER_SHAPE equ 3
|
||||||
|
VMMDEV_REQ_GET_MOUSE_STATUS_EX equ 223 ; Extended: + buttons, scroll
|
||||||
|
|
||||||
|
; VMMDEV_REQ_GET_POINTER_SHAPE equ 58
|
||||||
|
|
||||||
|
; Новые (v1_04):
|
||||||
|
VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE equ 0x00000001
|
||||||
|
VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR equ 0x00000002
|
||||||
|
VMMDEV_MOUSE_GUEST_HAS_ABSOLUTE equ 0x00000004
|
||||||
|
VMMDEV_MOUSE_NEW_PROTOCOL equ 0x00000010
|
||||||
|
VMMDEV_MOUSE_NOTIFY_GUEST equ 0x00000080
|
||||||
|
VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE equ 0x00000100
|
||||||
|
VMMDEV_MOUSE_HOST_HAS_ABSOLUTE equ 0x00000200
|
||||||
|
VMMDEV_MOUSE_HOST_NEW_PROTOCOL equ 0x00000400
|
||||||
|
|
||||||
|
; Full state protocol (VBox 6.1+): кнопки + скролл в одном запросе
|
||||||
|
VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL equ 0x00000080
|
||||||
|
VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL equ 0x00000100
|
||||||
|
|
||||||
|
; Mouse button masks
|
||||||
|
VMMDEV_MOUSE_BUTTON_LEFT equ 0x01
|
||||||
|
VMMDEV_MOUSE_BUTTON_RIGHT equ 0x02
|
||||||
|
VMMDEV_MOUSE_BUTTON_MIDDLE equ 0x04
|
||||||
|
VMMDEV_MOUSE_BUTTON_X1 equ 0x08
|
||||||
|
VMMDEV_MOUSE_BUTTON_X2 equ 0x10
|
||||||
|
|
||||||
|
VBOX_MOUSE_GUEST_FEATURES equ VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
|
||||||
|
VBOX_MOUSE_GUEST_FEATURES_EXT equ (VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE or VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL)
|
||||||
|
|
||||||
|
MOUSE_EVENT_MASK equ ( VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED or VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
|
||||||
|
|
||||||
|
VBOXGUEST_GUEST_CAPS_OR_MASK equ ( VBOXGUEST_GUEST_CAPS_OR_MASK or VMMDEV_GUEST_SUPPORTS_MOUSE )
|
||||||
|
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or MOUSE_EVENT_MASK )
|
||||||
43
data/mouse/structs.inc
Normal file
43
data/mouse/structs.inc
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Запрос: GetMouseStatus (VMMDevReq_GetMouseStatus = 1)
|
||||||
|
; Запрос: SetMouseStatus (VMMDevReq_SetMouseStatus = 2)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_REQ_MOUSE_STATUS
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
mouse_features dd ? ; VMMDEV_MOUSE_* features
|
||||||
|
pointer_x_pos dd ? ; X position
|
||||||
|
pointer_y_pos dd ? ; Y position
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Mouse pointer shape (для изменения курсора)
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_REQ_POINTER_SHAPE
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
flags dd ? ; VMMDEV_POINTER_* flags
|
||||||
|
x_hot dd ? ; X hotspot
|
||||||
|
y_hot dd ? ; Y hotspot
|
||||||
|
width dd ? ; Width in pixels
|
||||||
|
height dd ? ; Height in pixels
|
||||||
|
; За которыми следуют данные маски
|
||||||
|
ends
|
||||||
|
; =============================================================================
|
||||||
|
; Extended mouse status (VMMDevReq_GetMouseStatusEx = 223)
|
||||||
|
; Включает кнопки и скролл. Требует VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL.
|
||||||
|
; =============================================================================
|
||||||
|
struct VMMDEV_REQ_MOUSE_STATUS_EX
|
||||||
|
core VMMDEV_REQ_MOUSE_STATUS
|
||||||
|
scroll_dz dd ? ; vertical scroll delta
|
||||||
|
scroll_dw dd ? ; horizontal scroll delta
|
||||||
|
buttons dd ? ; VMMDEV_MOUSE_BUTTON_* mask
|
||||||
|
ends
|
||||||
|
|
||||||
|
;struct VMMDEV_REQ_MOUSE_POINTER
|
||||||
|
; header VMMDEV_HEADER
|
||||||
|
; f_flags dd ?
|
||||||
|
; x_hot dd ?
|
||||||
|
; y_hot dd ?
|
||||||
|
; width dd ?
|
||||||
|
; height dd ?
|
||||||
|
; data rb 0 ; AND-mask + XOR-mask (variable)
|
||||||
|
;ends
|
||||||
26
data/seamless/constants.inc
Normal file
26
data/seamless/constants.inc
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Seamless Constants — VBox 7.2.6
|
||||||
|
; Источник: include/VBox/VMMDevCoreTypes.h, include/VBox/VMMDev.h
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; VMMDevReq_GetSeamlessChangeRequest
|
||||||
|
VMMDEV_REQ_GET_SEAMLESS_CHANGE equ 73
|
||||||
|
|
||||||
|
; VMMDevSeamlessMode enum
|
||||||
|
VMMDEV_SEAMLESS_DISABLED equ 0 ; Обычный режим, весь десктоп
|
||||||
|
VMMDEV_SEAMLESS_VISIBLE_REGION equ 1 ; Только top-level окна
|
||||||
|
VMMDEV_SEAMLESS_HOST_WINDOW equ 2 ; Каждое окно гостя = окно хоста
|
||||||
|
|
||||||
|
; VMMDevReq_VideoSetVisibleRegion
|
||||||
|
VMMDEV_REQ_VIDEO_SET_VISIBLE_REGION equ 72
|
||||||
|
|
||||||
|
; Event: хост просит переключить seamless mode
|
||||||
|
; VMMDEV_EVENT_SEAMLESS_MODE_CHANGE = 0x00000020 (bit 5) — уже в core/constants.inc
|
||||||
|
|
||||||
|
SEAMLESS_EVENT_MASK equ VMMDEV_EVENT_SEAMLESS_MODE_CHANGE
|
||||||
|
|
||||||
|
; Caps: гость поддерживает seamless
|
||||||
|
; VMMDEV_GUEST_SUPPORTS_SEAMLESS = 0x00000001 — уже в core/constants.inc
|
||||||
|
|
||||||
|
; Накопительная маска
|
||||||
|
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or SEAMLESS_EVENT_MASK )
|
||||||
29
data/seamless/structs.inc
Normal file
29
data/seamless/structs.inc
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Seamless Structures — VBox 7.2.6
|
||||||
|
; Источник: include/VBox/VMMDev.h
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; VMMDevSeamlessChangeRequest (VMMDevReq_GetSeamlessChangeRequest = 73)
|
||||||
|
; size = 24 (header) + 8 = 32
|
||||||
|
struct VMMDEV_SEAMLESS_CHANGE_REQUEST
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
mode dd ? ; OUT: VMMDevSeamlessMode (0=disabled, 1=visible, 2=host_window)
|
||||||
|
eventAck dd ? ; IN: VMMDEV_EVENT_SEAMLESS_MODE_CHANGE для ACK
|
||||||
|
ends
|
||||||
|
|
||||||
|
; RTRECT — прямоугольник (для VideoSetVisibleRegion)
|
||||||
|
struct RTRECT
|
||||||
|
xLeft dd ?
|
||||||
|
yTop dd ?
|
||||||
|
xRight dd ?
|
||||||
|
yBottom dd ?
|
||||||
|
ends
|
||||||
|
|
||||||
|
; VMMDevVideoSetVisibleRegion (VMMDevReq_VideoSetVisibleRegion = 72)
|
||||||
|
; Переменная длина: header(24) + cRect(4) + Rect[cRect] (16 каждый)
|
||||||
|
; Для KolibriOS — один прямоугольник (весь экран)
|
||||||
|
struct VMMDEV_VIDEO_SET_VISIBLE_REGION
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
cRect dd ? ; Количество прямоугольников
|
||||||
|
rect0 RTRECT ; Первый (и единственный) прямоугольник
|
||||||
|
ends
|
||||||
113
data/shared_folders/constants.inc
Normal file
113
data/shared_folders/constants.inc
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; SharedFolder Constants (VirtualBox HGCM)
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; =========================================
|
||||||
|
; Функции службы Shared Folders (SHFL_FN_*)
|
||||||
|
; =========================================
|
||||||
|
SHFL_FN_QUERY_MAPPINGS equ 1 ; Запросить список подключенных папок
|
||||||
|
SHFL_FN_QUERY_MAP_NAME equ 2 ; Запросить имя подключения по индексу
|
||||||
|
SHFL_FN_CREATE equ 3 ; Создать файл/каталог
|
||||||
|
SHFL_FN_CLOSE equ 4 ; Закрыть дескриптор
|
||||||
|
SHFL_FN_READ equ 5 ; Чтение из файла
|
||||||
|
SHFL_FN_WRITE equ 6 ; Запись в файл
|
||||||
|
SHFL_FN_LOCK equ 7 ; Блокировка файла
|
||||||
|
SHFL_FN_LIST equ 8 ; Получить содержимое каталога
|
||||||
|
SHFL_FN_INFORMATION equ 9 ; Получить/установить информацию о файле
|
||||||
|
SHFL_FN_UNUSED_10 equ 10 ; (Зарезервировано)
|
||||||
|
SHFL_FN_REMOVE equ 11 ; Удалить файл/каталог
|
||||||
|
SHFL_FN_MAP_FOLDER_OLD equ 12 ; Устаревшее подключение папки (не использовать)
|
||||||
|
SHFL_FN_UNMAP_FOLDER equ 13 ; Отключить папку
|
||||||
|
SHFL_FN_RENAME equ 14 ; Переименовать/переместить
|
||||||
|
SHFL_FN_FLUSH equ 15 ; Сбросить кэш файла на диск
|
||||||
|
SHFL_FN_SET_UTF8 equ 16 ; Включить режим UTF-8 (0 параметров)
|
||||||
|
SHFL_FN_MAP_FOLDER equ 17 ; Подключить папку
|
||||||
|
SHFL_FN_READLINK equ 18 ; Прочитать символьную ссылку
|
||||||
|
SHFL_FN_SYMLINK equ 19 ; Создать символьную ссылку
|
||||||
|
SHFL_FN_SET_SYMLINKS equ 20 ; Разрешить/запретить симлинки (0 параметров)
|
||||||
|
SHFL_FN_SET_FILE_SIZE equ 24 ; Установить размер файла (усечь/расширить)
|
||||||
|
|
||||||
|
; Флаги для SHFL_FN_REMOVE
|
||||||
|
SHFL_REMOVE_FILE equ 0x01 ; Удалить файл
|
||||||
|
SHFL_REMOVE_DIR equ 0x02 ; Удалить каталог
|
||||||
|
SHFL_REMOVE_SYMLINK equ 0x04 ; Удалить символьную ссылку
|
||||||
|
|
||||||
|
; Флаги для SHFL_FN_RENAME
|
||||||
|
SHFL_RENAME_FILE equ 0x01 ; Переименовать файл
|
||||||
|
SHFL_RENAME_DIR equ 0x02 ; Переименовать каталог
|
||||||
|
SHFL_RENAME_REPLACE_IF_EXISTS equ 0x04 ; Заменить если существует
|
||||||
|
|
||||||
|
; Флаги для операции LIST (используются в SHFL_LIST_PARMS.Flags)
|
||||||
|
; ==============================================================
|
||||||
|
SHFL_LIST_NONE equ 0x00000000 ; Базовый листинг
|
||||||
|
SHFL_LIST_RETURN_ONE equ 0x00000001 ; Вернуть только первую запись
|
||||||
|
SHFL_LIST_RESTART equ 0x00000002 ; Начать перебор заново
|
||||||
|
SHFL_LIST_SIZE_RETURNED equ 0x00000004 ; В SizeReturned реальный размер данных
|
||||||
|
|
||||||
|
; Лимиты
|
||||||
|
; ======
|
||||||
|
SHFL_MAX_MAPPINGS equ 10 ; Максимальное число подключенных папок
|
||||||
|
SHFL_MAX_NAME_LEN equ 256 ; Макс. длина имени в символах
|
||||||
|
SHFL_MAX_PATH_LEN equ 4096 ; Макс. длина пути в байтах (включая UTF-8)
|
||||||
|
|
||||||
|
; Флаги создания/открытия файлов - SHFL_CF_*
|
||||||
|
; ======================================================================
|
||||||
|
; SHFL_CF_* — CreateFlags для SHFL_FN_CREATE (из shflsvc.h)
|
||||||
|
; ======================================================================
|
||||||
|
SHFL_CF_NONE equ 0x00000000
|
||||||
|
|
||||||
|
; Бит 0: Lookup only
|
||||||
|
SHFL_CF_LOOKUP equ 0x00000001
|
||||||
|
|
||||||
|
; Бит 1: Open target directory
|
||||||
|
SHFL_CF_OPEN_TARGET_DIRECTORY equ 0x00000002
|
||||||
|
|
||||||
|
; Бит 2: Object is a directory
|
||||||
|
SHFL_CF_DIRECTORY equ 0x00000004
|
||||||
|
|
||||||
|
; Биты 4..7: Action if file EXISTS
|
||||||
|
SHFL_CF_ACT_MASK_IF_EXISTS equ 0x000000F0
|
||||||
|
SHFL_CF_ACT_OPEN_IF_EXISTS equ 0x00000000 ; Open existing
|
||||||
|
SHFL_CF_ACT_FAIL_IF_EXISTS equ 0x00000010 ; Fail if exists
|
||||||
|
SHFL_CF_ACT_REPLACE_IF_EXISTS equ 0x00000020 ; Replace (truncate)
|
||||||
|
SHFL_CF_ACT_OVERWRITE_IF_EXISTS equ 0x00000030 ; Overwrite
|
||||||
|
|
||||||
|
; Биты 8..11: Action if file is NEW
|
||||||
|
SHFL_CF_ACT_MASK_IF_NEW equ 0x00000F00
|
||||||
|
SHFL_CF_ACT_CREATE_IF_NEW equ 0x00000000 ; Create new file
|
||||||
|
SHFL_CF_ACT_FAIL_IF_NEW equ 0x00000100 ; Fail if doesn't exist
|
||||||
|
|
||||||
|
; Биты 12..13: Access mode (read/write)
|
||||||
|
SHFL_CF_ACCESS_MASK_RW equ 0x00003000
|
||||||
|
SHFL_CF_ACCESS_NONE equ 0x00000000
|
||||||
|
SHFL_CF_ACCESS_READ equ 0x00001000
|
||||||
|
SHFL_CF_ACCESS_WRITE equ 0x00002000
|
||||||
|
SHFL_CF_ACCESS_READWRITE equ 0x00003000
|
||||||
|
|
||||||
|
; Биты 14..15: Deny mode (sharing)
|
||||||
|
SHFL_CF_ACCESS_MASK_DENY equ 0x0000C000
|
||||||
|
SHFL_CF_ACCESS_DENYNONE equ 0x00000000
|
||||||
|
SHFL_CF_ACCESS_DENYREAD equ 0x00004000
|
||||||
|
SHFL_CF_ACCESS_DENYWRITE equ 0x00008000
|
||||||
|
SHFL_CF_ACCESS_DENYALL equ 0x0000C000
|
||||||
|
|
||||||
|
; Биты 16..17: Attribute access
|
||||||
|
SHFL_CF_ACCESS_MASK_ATTR equ 0x00030000
|
||||||
|
SHFL_CF_ACCESS_ATTR_NONE equ 0x00000000
|
||||||
|
SHFL_CF_ACCESS_ATTR_READ equ 0x00010000
|
||||||
|
SHFL_CF_ACCESS_ATTR_WRITE equ 0x00020000
|
||||||
|
SHFL_CF_ACCESS_ATTR_READWRITE equ 0x00030000
|
||||||
|
|
||||||
|
; Бит 18: Append mode
|
||||||
|
SHFL_CF_ACCESS_APPEND equ 0x00040000
|
||||||
|
|
||||||
|
; ======================================================================
|
||||||
|
; SHFLCREATERESULT — результат CREATE
|
||||||
|
; ======================================================================
|
||||||
|
SHFL_NO_RESULT equ 0
|
||||||
|
SHFL_PATH_NOT_FOUND equ 1
|
||||||
|
SHFL_FILE_NOT_FOUND equ 2
|
||||||
|
SHFL_FILE_EXISTS equ 3
|
||||||
|
SHFL_FILE_CREATED equ 4
|
||||||
|
SHFL_FILE_REPLACED equ 5
|
||||||
|
|
||||||
304
data/shared_folders/structs.inc
Normal file
304
data/shared_folders/structs.inc
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; VirtualBox SharedFolder Structures
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Глобальное состояние SharedFolder драйвера
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SF_STATE
|
||||||
|
connected dd ? ; 1 = HGCM подключен к сервису
|
||||||
|
client_id dd ? ; HGCM client ID
|
||||||
|
|
||||||
|
; Динамически выделяемые init-буферы
|
||||||
|
packet_ptr dd ? ; sf_packet (KernelAlloc 4096)
|
||||||
|
mappings_ptr dd ? ; sf_mappings (часть small_bufs)
|
||||||
|
namebuf_ptr dd ? ; sf_namebuf
|
||||||
|
cp866_buf_ptr dd ? ; sf_cp866_buf
|
||||||
|
listbuf_ptr dd ? ; sf_listbuf (KernelAlloc 4096)
|
||||||
|
createparms_ptr dd ? ; sf_createparms
|
||||||
|
dir_path_ptr dd ? ; sf_dir_path
|
||||||
|
small_bufs_ptr dd ? ; единый блок мелких буферов (KernelAlloc 4096)
|
||||||
|
|
||||||
|
; Динамически выделяемые FS-буферы
|
||||||
|
fs_packet_ptr dd ? ; vboxsf_fs_packet (KernelAlloc 4096)
|
||||||
|
fs_listbuf_ptr dd ? ; vboxsf_fs_listbuf (KernelAlloc 4096)
|
||||||
|
fs_iobuf_ptr dd ? ; vboxsf_fs_iobuf (KernelAlloc 65536)
|
||||||
|
fs_cparms_ptr dd ? ; vboxsf_fs_cparms (KernelAlloc 4096)
|
||||||
|
fs_pathbuf_ptr dd ? ; vboxsf_fs_pathbuf
|
||||||
|
fs_pathbuf2_ptr dd ? ; vboxsf_fs_pathbuf2
|
||||||
|
fs_tmpname_ptr dd ? ; vboxsf_fs_tmpname
|
||||||
|
fs_small_bufs_ptr dd ? ; единый блок мелких FS-буферов (KernelAlloc 4096)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Информация об одной shared folder (выделяется через KernelAlloc)
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SF_FOLDER
|
||||||
|
root_handle dd ? ; SHFL root handle (от MAP_FOLDER)
|
||||||
|
active dd ? ; 1 = folder активен и подключен
|
||||||
|
name rb 256 ; Имя mapping (UTF-8, null-terminated)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Данные для виртуального диска /vbox/
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct VBOXSF_DISK
|
||||||
|
folders rd SHFL_MAX_FOLDERS ; Указатели на SF_FOLDER
|
||||||
|
count dd ? ; Количество папок
|
||||||
|
disk_handle dd ? ; Handle от DiskAdd
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Структура DISKMEDIAINFO для querymedia callback
|
||||||
|
; =============================================================================
|
||||||
|
struct DISKMEDIAINFO
|
||||||
|
flags dd ? ; флаги медиа
|
||||||
|
sectorsize dd ? ; размер сектора
|
||||||
|
capacity dq ? ; емкость в секторах (64-bit)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Смещения внутренних структур ядра KolibriOS
|
||||||
|
; (нужны для create_partition callback)
|
||||||
|
; =============================================================================
|
||||||
|
KPARTITION_OFS_DISK = 16 ; PARTITION.Disk -> DISK*
|
||||||
|
KPARTITION_OFS_FSUSERFUNCTIONS = 20 ; PARTITION.FSUserFunctions -> UserFuncs*
|
||||||
|
KDISK_OFS_USERDATA = 16 ; DISK.UserData -> void*
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; BDFE (Block Data File Entry) — формат записи в директории KolibriOS
|
||||||
|
; =============================================================================
|
||||||
|
BDFE_SIZE = 304 ; Размер одной BDFE записи (CP866)
|
||||||
|
BDFE_HEADER_SIZE = 32 ; Размер заголовка ответа ReadFolder
|
||||||
|
|
||||||
|
; Структура заголовка ReadFolder ответа
|
||||||
|
struct BDFE_HEADER
|
||||||
|
version dd ? ; Версия формата (1)
|
||||||
|
entries_placed dd ? ; Количество возвращенных записей
|
||||||
|
total_entries dd ? ; Общее количество записей
|
||||||
|
reserved rd 5 ; Зарезервировано (20 байт)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Смещения полей BDFE
|
||||||
|
BDFE_OFS_ATTR = 0 ; dword: атрибуты
|
||||||
|
BDFE_OFS_ENCODING = 4 ; byte: кодировка (0=CP866, 1=UTF-16, 2=UTF-8)
|
||||||
|
BDFE_OFS_CTIME = 8 ; dword: время создания
|
||||||
|
BDFE_OFS_CDATE = 12 ; dword: дата создания
|
||||||
|
BDFE_OFS_ATIME = 16 ; dword: время доступа
|
||||||
|
BDFE_OFS_ADATE = 20 ; dword: дата доступа
|
||||||
|
BDFE_OFS_MTIME = 24 ; dword: время модификации
|
||||||
|
BDFE_OFS_MDATE = 28 ; dword: дата модификации
|
||||||
|
BDFE_OFS_SIZE_LO = 32 ; dword: размер файла (младшие 32 бита)
|
||||||
|
BDFE_OFS_SIZE_HI = 36 ; dword: размер файла (старшие 32 бита)
|
||||||
|
BDFE_OFS_NAME = 40 ; 264 байта: имя файла (null-terminated)
|
||||||
|
|
||||||
|
; Атрибуты файлов
|
||||||
|
FA_READONLY = 0x01
|
||||||
|
FA_HIDDEN = 0x02
|
||||||
|
FA_SYSTEM = 0x04
|
||||||
|
FA_LABEL = 0x08
|
||||||
|
FA_FOLDER = 0x10
|
||||||
|
FA_ARCHIVED = 0x20
|
||||||
|
|
||||||
|
; Коды ошибок KolibriOS (syscall 70)
|
||||||
|
ERROR_SUCCESS = 0
|
||||||
|
ERROR_DISK_FULL = 1
|
||||||
|
ERROR_UNSUPPORTED = 2
|
||||||
|
ERROR_UNKNOWN_FS = 3
|
||||||
|
ERROR_FILE_NOT_FOUND = 5
|
||||||
|
ERROR_END_OF_FILE = 6
|
||||||
|
ERROR_MEMORY = 7
|
||||||
|
ERROR_ACCESS_DENIED = 10
|
||||||
|
ERROR_DEVICE = 11
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Смещения внутри SHFLFSOBJINFO (92 байта)
|
||||||
|
; Используются для конвертации в BDFE при ReadFolder/GetFileInfo
|
||||||
|
; =============================================================================
|
||||||
|
SHFLOBJINFO_OFS_SIZE_LO = 0 ; int64 cbObject (low)
|
||||||
|
SHFLOBJINFO_OFS_SIZE_HI = 4 ; int64 cbObject (high)
|
||||||
|
SHFLOBJINFO_OFS_ALLOC_LO = 8 ; int64 cbAllocated (low)
|
||||||
|
SHFLOBJINFO_OFS_ALLOC_HI = 12 ; int64 cbAllocated (high)
|
||||||
|
SHFLOBJINFO_OFS_BTIME_LO = 16 ; int64 BirthTime (low, nanosec)
|
||||||
|
SHFLOBJINFO_OFS_BTIME_HI = 20 ; int64 BirthTime (high)
|
||||||
|
SHFLOBJINFO_OFS_CTIME_LO = 24 ; int64 ChangeTime (low)
|
||||||
|
SHFLOBJINFO_OFS_CTIME_HI = 28 ; int64 ChangeTime (high)
|
||||||
|
SHFLOBJINFO_OFS_MTIME_LO = 32 ; int64 ModificationTime (low)
|
||||||
|
SHFLOBJINFO_OFS_MTIME_HI = 36 ; int64 ModificationTime (high)
|
||||||
|
SHFLOBJINFO_OFS_ATIME_LO = 40 ; int64 AccessTime (low)
|
||||||
|
SHFLOBJINFO_OFS_ATIME_HI = 44 ; int64 AccessTime (high)
|
||||||
|
SHFLOBJINFO_OFS_FMODE = 48 ; uint32 Attr (RTFMODE)
|
||||||
|
|
||||||
|
; Биты fMode (RTFMODE)
|
||||||
|
S_IFDIR = 0x4000
|
||||||
|
S_IFREG = 0x8000
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; HGCM пакеты для SharedFolder операций
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_QUERY_MAPPINGS - Получить список доступных mappings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_QUERY_MAPPINGS
|
||||||
|
header HGCM_CALL
|
||||||
|
flags HGCM_PARM ; IN: флаги (обычно 0)
|
||||||
|
count HGCM_PARM ; OUT: количество mappings
|
||||||
|
buffer HGCM_PARM ; OUT: буфер для данных (SHFLMAPPING[])
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_QUERY_MAP_NAME - Получить имя mapping по индексу
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_QUERY_MAP_NAME
|
||||||
|
header HGCM_CALL
|
||||||
|
index HGCM_PARM ; IN: индекс mapping (0..count-1)
|
||||||
|
name HGCM_PARM ; OUT: имя mapping (SHFLSTRING)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_MAP_FOLDER - Подключить mapping
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_MAP_FOLDER
|
||||||
|
header HGCM_CALL
|
||||||
|
path HGCM_PARM ; IN
|
||||||
|
root HGCM_PARM ; OUT
|
||||||
|
delimiter HGCM_PARM ; IN
|
||||||
|
flags HGCM_PARM ; IN
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_UNMAP_FOLDER - Отключить mapping
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_UNMAP_FOLDER
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_CREATE - Открыть/создать файл
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_CREATE
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
path HGCM_PARM ; IN: путь к файлу (SHFLSTRING)
|
||||||
|
parms HGCM_PARM ; IN/OUT: параметры (SHFLCREATEPARMS)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; Параметры создания файла (108 байт)
|
||||||
|
struct SHFLCREATEPARMS
|
||||||
|
handle_lo dd ? ; +0: OUT: SHFLHANDLE low (uint64_t)
|
||||||
|
handle_hi dd ? ; +4: OUT: SHFLHANDLE high
|
||||||
|
result dd ? ; +8: OUT: SHFLCREATERESULT
|
||||||
|
flags dd ? ; +12: IN: SHFL_CF_* CreateFlags
|
||||||
|
info rb 92 ; +16: IN/OUT: SHFLFSOBJINFO
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFLDIRINFO - одна запись в результате SHFL_FN_LIST (переменный размер)
|
||||||
|
; Фиксированная часть = 126 байт:
|
||||||
|
; SHFLFSOBJINFO (92) + cucShortName (2) + uszShortName (28) + SHFLSTRING hdr (4)
|
||||||
|
; За ней идут байты имени файла (переменная длина).
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
SHFLDIRINFO_FIXED_SIZE = 126
|
||||||
|
|
||||||
|
SHFLDIRINFO_OFS_INFO = 0 ; SHFLFSOBJINFO (92 bytes)
|
||||||
|
SHFLDIRINFO_OFS_SIZE_LO = 0 ; cbObject low
|
||||||
|
SHFLDIRINFO_OFS_SIZE_HI = 4 ; cbObject high
|
||||||
|
SHFLDIRINFO_OFS_FMODE = 48 ; fMode (RTFMODE)
|
||||||
|
SHFLDIRINFO_OFS_CUC_SHORT_NAME = 92 ; cucShortName (uint16)
|
||||||
|
SHFLDIRINFO_OFS_USZ_SHORT_NAME = 94 ; uszShortName (14 x uint16 = 28 bytes)
|
||||||
|
SHFLDIRINFO_OFS_NAME_SIZE = 122 ; SHFLSTRING.u16Size
|
||||||
|
SHFLDIRINFO_OFS_NAME_LENGTH = 124 ; SHFLSTRING.u16Length
|
||||||
|
SHFLDIRINFO_OFS_NAME_STRING = 126 ; SHFLSTRING.string (данные имени)
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_CLOSE - Закрыть файл
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_CLOSE
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
handle HGCM_PARM ; IN: file handle
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_READ - Читать из файла
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_READ
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
handle HGCM_PARM ; IN: file handle
|
||||||
|
offset_low HGCM_PARM ; IN: offset low (64-bit)
|
||||||
|
offset_high HGCM_PARM ; IN: offset high (64-bit)
|
||||||
|
size HGCM_PARM ; IN/OUT: размер для чтения
|
||||||
|
buffer HGCM_PARM ; OUT: буфер для данных
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_WRITE - Писать в файл
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_WRITE
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
handle HGCM_PARM ; IN: file handle
|
||||||
|
offset_low HGCM_PARM ; IN: offset low (64-bit)
|
||||||
|
offset_high HGCM_PARM ; IN: offset high (64-bit)
|
||||||
|
size HGCM_PARM ; IN/OUT: размер для записи
|
||||||
|
buffer HGCM_PARM ; IN: буфер с данными
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_LIST - Получить список файлов в директории
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_LIST
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; parm[0] IN: root handle (32bit)
|
||||||
|
handle HGCM_PARM ; parm[1] IN: dir handle (64bit!)
|
||||||
|
flags HGCM_PARM ; parm[2] IN: SHFL_LIST_* (32bit)
|
||||||
|
cb HGCM_PARM ; parm[3] IN/OUT: размер буфера (32bit)
|
||||||
|
path HGCM_PARM ; parm[4] IN: search pattern (опционально)
|
||||||
|
buffer HGCM_PARM ; parm[5] OUT: SHFLDIRINFO[]
|
||||||
|
resume_pt HGCM_PARM ; parm[6] IN/OUT: точка продолжения (32bit)
|
||||||
|
file_count HGCM_PARM ; parm[7] OUT: количество файлов (32bit)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_INFORMATION - Получить информацию о файле
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_INFORMATION
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
handle HGCM_PARM ; IN: file handle
|
||||||
|
flags HGCM_PARM ; IN: флаги
|
||||||
|
info HGCM_PARM ; OUT: информация (SHFLFSOBJINFO)
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_REMOVE - Удалить файл
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_REMOVE
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
path HGCM_PARM ; IN: путь к файлу (SHFLSTRING)
|
||||||
|
flags HGCM_PARM ; IN: флаги
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_RENAME - Переименовать файл
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_RENAME
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
src HGCM_PARM ; IN: исходный путь (SHFLSTRING)
|
||||||
|
dst HGCM_PARM ; IN: новый путь (SHFLSTRING)
|
||||||
|
flags HGCM_PARM ; IN: флаги
|
||||||
|
ends
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; SHFL_FN_FLUSH - Flush буферов файла
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
struct SHFL_FLUSH
|
||||||
|
header HGCM_CALL
|
||||||
|
root HGCM_PARM ; IN: root handle
|
||||||
|
handle HGCM_PARM ; IN: file handle
|
||||||
|
ends
|
||||||
16
data/timesync/constants.inc
Normal file
16
data/timesync/constants.inc
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Time Sync Constants
|
||||||
|
; =============================================================================
|
||||||
|
VMMDEV_REQ_GET_HOST_TIME equ 10 ; VMMDevReq_GetHostTime
|
||||||
|
|
||||||
|
; Интервал синхронизации: 6000 тиков * 10ms = 60 сек
|
||||||
|
TIMESYNC_INTERVAL_TICKS equ 6000
|
||||||
|
|
||||||
|
; Максимальное допустимое расхождение (100ms в наносекундах / 100)
|
||||||
|
TIMESYNC_MAX_DRIFT_NS100 equ 1000000
|
||||||
|
|
||||||
|
; Флаг: нет событий VMMDev, чисто по таймеру
|
||||||
|
TIMESYNC_EVENT_MASK equ 0
|
||||||
|
|
||||||
|
; Накопительные маски (timesync не требует событий и caps)
|
||||||
|
VBOXGUEST_EVENTS_OR_MASK equ ( VBOXGUEST_EVENTS_OR_MASK or TIMESYNC_EVENT_MASK )
|
||||||
12
data/timesync/structs.inc
Normal file
12
data/timesync/structs.inc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Time Sync Structures
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; VMMDevReq_GetHostTime (request type 10)
|
||||||
|
; Возвращает время хоста в формате 100-наносекундных интервалов с 01.01.1601
|
||||||
|
; (Windows FILETIME формат, он же RTTIMESPEC в VBox)
|
||||||
|
struct VMMDEV_GET_HOST_TIME
|
||||||
|
header VMMDEV_HEADER
|
||||||
|
time_low dd ? ; Младшие 32 бита (100ns units since 1601)
|
||||||
|
time_high dd ? ; Старшие 32 бита
|
||||||
|
ends
|
||||||
63
hgcm/async.inc
Normal file
63
hgcm/async.inc
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : HGCM Async
|
||||||
|
; Назначение : Ожидание завершения async HGCM запросов
|
||||||
|
; Файл : hgcm/async.inc
|
||||||
|
; Версия : 1.0
|
||||||
|
; Дата : 2025.01.15
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; hgcm_wait_async
|
||||||
|
proc hgcm_wait_async uses ebx ecx edx esi, request_ptr:dword
|
||||||
|
mov esi, [request_ptr]
|
||||||
|
test esi, esi
|
||||||
|
jz .bad
|
||||||
|
|
||||||
|
mov ecx, HGCM_TIMEOUT_ITERS
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Waiting for async completion (timeout=%d iterations)\n", HGCM_TIMEOUT_ITERS
|
||||||
|
|
||||||
|
.wait_loop:
|
||||||
|
mfence
|
||||||
|
mov eax, [esi + HGCM_HEADER.flags]
|
||||||
|
|
||||||
|
test eax, VBOX_HGCM_REQ_DONE
|
||||||
|
jnz .completed
|
||||||
|
|
||||||
|
test eax, VBOX_HGCM_REQ_CANCELLED
|
||||||
|
jnz .cancelled
|
||||||
|
|
||||||
|
dec ecx
|
||||||
|
jz .timeout
|
||||||
|
|
||||||
|
; короткая задержка
|
||||||
|
push ecx
|
||||||
|
mov ecx, 1000
|
||||||
|
.delay:
|
||||||
|
pause
|
||||||
|
loop .delay
|
||||||
|
pop ecx
|
||||||
|
|
||||||
|
jmp .wait_loop
|
||||||
|
|
||||||
|
.completed:
|
||||||
|
mfence
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Async request completed\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.cancelled:
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Async request cancelled\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
|
||||||
|
.timeout:
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] *** ASYNC TIMEOUT ***\n"
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Current flags: 0x%x\n", [esi + HGCM_HEADER.flags]
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Request RC: 0x%x\n", [esi + HGCM_HEADER.header.rc]
|
||||||
|
mov eax, VERR_TIMEOUT
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
395
hgcm/call.inc
Normal file
395
hgcm/call.inc
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : HGCM Call32 с поддержкой PageList параметров
|
||||||
|
; Назначение : HGCM вызовы (CALL32) поверх hgcm_send_request
|
||||||
|
; Автоматическая конвертация LINADDR -> PAGE_LIST (type=10)
|
||||||
|
; Файл : hgcm/call.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_call32_pagelist — вызов HGCM_CALL32 с auto-pagelist
|
||||||
|
;
|
||||||
|
; Вход : client_id, function, parms_ptr, parm_count
|
||||||
|
; Выход: eax = 0 успех / VERR_*
|
||||||
|
;
|
||||||
|
; LINADDR_IN/OUT/INOUT автоматически конвертируются в PAGE_LIST (type=10).
|
||||||
|
; 32BIT/64BIT передаются как есть.
|
||||||
|
; После вызова: данные OUT/INOUT уже в памяти гостя (хост писал напрямую
|
||||||
|
; в физ. страницы). Обновляется поле size в оригинальных параметрах.
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_call32_pagelist uses ebx ecx edx esi edi, client_id:dword, function:dword, parms:dword, parm_count:dword
|
||||||
|
locals
|
||||||
|
packet_base dd ?
|
||||||
|
data_tail dd ? ; текущее смещение хвоста (для PageListInfo)
|
||||||
|
param_idx dd ?
|
||||||
|
; Временные переменные для конвертации одного параметра
|
||||||
|
conv_cbdata dd ? ; размер буфера
|
||||||
|
conv_vaddr dd ? ; виртуальный адрес буфера
|
||||||
|
conv_flags dd ? ; флаги направления
|
||||||
|
conv_off_first dd ? ; смещение в первой странице
|
||||||
|
conv_cpages dd ? ; количество страниц
|
||||||
|
conv_page_virt dd ? ; текущий virt addr страницы (для цикла)
|
||||||
|
endl
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] CALL32: client=0x%x fn=%d cnt=%d\n", \
|
||||||
|
[client_id], [function], [parm_count]
|
||||||
|
|
||||||
|
; --- Проверка: буфер не занят (защита от реентрантности) ---
|
||||||
|
; lock bts dword [hgcm_pl_busy], 0
|
||||||
|
; jc .busy
|
||||||
|
|
||||||
|
; --- Проверки ---
|
||||||
|
mov eax, [parm_count]
|
||||||
|
cmp eax, VMMDEV_MAX_HGCM_PARMS
|
||||||
|
jbe .count_ok
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] ERROR: parm_count %d > max %d\n", eax, VMMDEV_MAX_HGCM_PARMS
|
||||||
|
; mov dword [hgcm_pl_busy], 0
|
||||||
|
mov eax, VERR_INVALID_PARAMETER
|
||||||
|
ret
|
||||||
|
.count_ok:
|
||||||
|
|
||||||
|
mov edi, [vbox_device.hgcm_call_pl_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad
|
||||||
|
mov [packet_base], edi
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 1. Заголовок HGCM_CALL
|
||||||
|
; =================================================================
|
||||||
|
mov eax, [parm_count]
|
||||||
|
imul eax, sizeof.HGCM_PARM
|
||||||
|
add eax, sizeof.HGCM_CALL
|
||||||
|
|
||||||
|
hgcm_prepare_header edi, eax, VMMDEV_HGCM_CALL32
|
||||||
|
|
||||||
|
mov eax, [client_id]
|
||||||
|
mov [edi + HGCM_CALL.client_id], eax
|
||||||
|
mov eax, [function]
|
||||||
|
mov [edi + HGCM_CALL.function], eax
|
||||||
|
mov eax, [parm_count]
|
||||||
|
mov [edi + HGCM_CALL.param_count], eax
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 2. Копируем параметры из parms[] в пакет
|
||||||
|
; =================================================================
|
||||||
|
mov ecx, [parm_count]
|
||||||
|
test ecx, ecx
|
||||||
|
jz .send_no_params
|
||||||
|
|
||||||
|
push ecx
|
||||||
|
mov esi, [parms]
|
||||||
|
lea edi, [edi + sizeof.HGCM_CALL]
|
||||||
|
imul ecx, sizeof.HGCM_PARM
|
||||||
|
rep movsb
|
||||||
|
pop ecx
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 3. data_tail = начало области для PageListInfo
|
||||||
|
; =================================================================
|
||||||
|
mov eax, [parm_count]
|
||||||
|
imul eax, sizeof.HGCM_PARM
|
||||||
|
add eax, sizeof.HGCM_CALL
|
||||||
|
mov [data_tail], eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] PageList tail starts at offset %d\n", eax
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 4. Конвертация LINADDR -> PAGE_LIST
|
||||||
|
; =================================================================
|
||||||
|
mov dword [param_idx], 0
|
||||||
|
|
||||||
|
.convert_loop:
|
||||||
|
mov ecx, [param_idx]
|
||||||
|
cmp ecx, [parm_count]
|
||||||
|
jae .convert_done
|
||||||
|
|
||||||
|
; esi -> текущий параметр в пакете
|
||||||
|
mov esi, [packet_base]
|
||||||
|
add esi, sizeof.HGCM_CALL
|
||||||
|
mov eax, ecx
|
||||||
|
imul eax, sizeof.HGCM_PARM
|
||||||
|
add esi, eax
|
||||||
|
|
||||||
|
mov eax, [esi + HGCM_PARM.type]
|
||||||
|
|
||||||
|
cmp eax, HGCM_PARM_TYPE_LINADDR_IN
|
||||||
|
je .conv_in
|
||||||
|
cmp eax, HGCM_PARM_TYPE_LINADDR_OUT
|
||||||
|
je .conv_out
|
||||||
|
cmp eax, HGCM_PARM_TYPE_LINADDR_INOUT
|
||||||
|
je .conv_inout
|
||||||
|
cmp eax, HGCM_PARM_TYPE_LINADDR
|
||||||
|
je .conv_inout
|
||||||
|
|
||||||
|
; Не LINADDR — пропускаем
|
||||||
|
jmp .next_param
|
||||||
|
|
||||||
|
.conv_in:
|
||||||
|
mov dword [conv_flags], HGCM_PL_FLAG_IN
|
||||||
|
jmp .do_convert
|
||||||
|
.conv_out:
|
||||||
|
mov dword [conv_flags], HGCM_PL_FLAG_OUT
|
||||||
|
jmp .do_convert
|
||||||
|
.conv_inout:
|
||||||
|
mov dword [conv_flags], HGCM_PL_FLAG_BOTH
|
||||||
|
|
||||||
|
; ---- Конвертация одного параметра в PAGE_LIST ----
|
||||||
|
; esi = указатель на HGCM_PARM в пакете (сохраняется)
|
||||||
|
.do_convert:
|
||||||
|
; Сохраняем cbData и vaddr в локальные переменные
|
||||||
|
mov eax, dword [esi + HGCM_PARM.u.LinAddr.size]
|
||||||
|
mov [conv_cbdata], eax
|
||||||
|
mov eax, dword [esi + HGCM_PARM.u.LinAddr.offset]
|
||||||
|
mov [conv_vaddr], eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] parm[%d]: LINADDR->PL vaddr=0x%x cb=%d fl=%d\n", \
|
||||||
|
[param_idx], [conv_vaddr], [conv_cbdata], [conv_flags]
|
||||||
|
|
||||||
|
; --- cbData == 0 -> пустой PageList ---
|
||||||
|
cmp dword [conv_cbdata], 0
|
||||||
|
je .empty_pagelist
|
||||||
|
|
||||||
|
; --- offFirstPage = vaddr & 0xFFF ---
|
||||||
|
mov eax, [conv_vaddr]
|
||||||
|
and eax, PAGE_OFFSET_MASK
|
||||||
|
mov [conv_off_first], eax
|
||||||
|
|
||||||
|
; --- cPages = (offFirstPage + cbData + 4095) >> 12 ---
|
||||||
|
mov eax, [conv_off_first]
|
||||||
|
add eax, [conv_cbdata]
|
||||||
|
add eax, PAGE_SIZE - 1
|
||||||
|
shr eax, 12
|
||||||
|
mov [conv_cpages], eax
|
||||||
|
|
||||||
|
; --- Проверка: хватает ли места в хвосте? ---
|
||||||
|
; Нужно: HGCM_PLI_APAGES (=8) + conv_cpages * 8
|
||||||
|
mov eax, [conv_cpages]
|
||||||
|
shl eax, 3 ; * 8 (размер одного RTGCPHYS64)
|
||||||
|
add eax, HGCM_PLI_APAGES ; + 8 (заголовок PageListInfo)
|
||||||
|
add eax, [data_tail]
|
||||||
|
cmp eax, HGCM_PL_BUF_SIZE
|
||||||
|
ja .buffer_overflow
|
||||||
|
|
||||||
|
; --- Заполняем заголовок HGCMPageListInfo ---
|
||||||
|
; edi -> PageListInfo в хвосте пакета
|
||||||
|
mov edi, [packet_base]
|
||||||
|
add edi, [data_tail]
|
||||||
|
|
||||||
|
; flags (direction)
|
||||||
|
mov eax, [conv_flags]
|
||||||
|
mov dword [edi + HGCM_PLI_FLAGS], eax
|
||||||
|
|
||||||
|
; offFirstPage (word) — смещение данных внутри первой страницы
|
||||||
|
mov eax, [conv_off_first]
|
||||||
|
mov word [edi + HGCM_PLI_OFF_FIRST_PAGE], ax
|
||||||
|
|
||||||
|
; cPages (word)
|
||||||
|
mov eax, [conv_cpages]
|
||||||
|
mov word [edi + HGCM_PLI_CPAGES], ax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] PLI at +%d: fl=%d offFirst=%d cPages=%d\n", \
|
||||||
|
[data_tail], [conv_flags], [conv_off_first], [conv_cpages]
|
||||||
|
|
||||||
|
; --- Заполняем массив aPages[] физическими адресами ---
|
||||||
|
; Подготовка: стартовый виртуальный адрес страницы
|
||||||
|
mov eax, [conv_vaddr]
|
||||||
|
and eax, PAGE_BASE_MASK
|
||||||
|
mov [conv_page_virt], eax ; virt base первой страницы
|
||||||
|
|
||||||
|
xor ecx, ecx ; ecx = индекс страницы
|
||||||
|
|
||||||
|
.page_loop:
|
||||||
|
cmp ecx, [conv_cpages]
|
||||||
|
jae .pages_done
|
||||||
|
|
||||||
|
; Получаем физический адрес текущей страницы
|
||||||
|
; GetPhysAddr: eax(вход) = virt addr, eax(выход) = phys addr
|
||||||
|
; Сохраняем всё, что может быть затёрто invoke
|
||||||
|
push ecx esi edi
|
||||||
|
mov eax, [conv_page_virt]
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov ebx, eax ; ebx = phys addr
|
||||||
|
pop edi esi ecx
|
||||||
|
|
||||||
|
; Записываем aPages[ecx] как RTGCPHYS64 (8 байт, little-endian)
|
||||||
|
mov dword [edi + HGCM_PLI_APAGES + ecx*8], ebx ; low 32 bits
|
||||||
|
mov dword [edi + HGCM_PLI_APAGES + ecx*8 + 4], 0 ; high 32 bits = 0
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] aPages[%d]: virt=0x%x phys=0x%x\n", \
|
||||||
|
ecx, [conv_page_virt], ebx
|
||||||
|
|
||||||
|
; Следующая страница
|
||||||
|
add dword [conv_page_virt], PAGE_SIZE
|
||||||
|
inc ecx
|
||||||
|
jmp .page_loop
|
||||||
|
|
||||||
|
.pages_done:
|
||||||
|
; --- Перезаписываем параметр в пакете как PAGE_LIST ---
|
||||||
|
; type = 10 (PAGE_LIST)
|
||||||
|
mov dword [esi + HGCM_PARM.type], HGCM_PARM_TYPE_PAGELIST
|
||||||
|
|
||||||
|
; u[0..3] = cbData
|
||||||
|
mov eax, [conv_cbdata]
|
||||||
|
mov dword [esi + HGCM_PARM.u], eax
|
||||||
|
|
||||||
|
; u[4..7] = смещение к PageListInfo от начала пакета
|
||||||
|
mov eax, [data_tail]
|
||||||
|
mov dword [esi + HGCM_PARM.u + 4], eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] => type=10 cb=%d offPLI=%d\n", \
|
||||||
|
[conv_cbdata], [data_tail]
|
||||||
|
|
||||||
|
; Сдвигаем data_tail: += HGCM_PLI_APAGES + cPages * 8
|
||||||
|
mov eax, [conv_cpages]
|
||||||
|
shl eax, 3 ; * 8
|
||||||
|
add eax, HGCM_PLI_APAGES ; + 8 (заголовок)
|
||||||
|
add [data_tail], eax
|
||||||
|
|
||||||
|
jmp .next_param
|
||||||
|
|
||||||
|
; --- Пустой PageList (cbData == 0) ---
|
||||||
|
; ВАЖНО: VMMDev проверяет cPages > 0 даже при cbData=0!
|
||||||
|
; Ставим cPages=1 с фиктивной страницей, чтобы пройти валидацию.
|
||||||
|
; cbData=0 гарантирует что данные не передаются.
|
||||||
|
.empty_pagelist:
|
||||||
|
; Проверка места: 16 байт (8 заголовок + 8 одна фиктивная страница)
|
||||||
|
mov eax, [data_tail]
|
||||||
|
add eax, HGCM_PLI_APAGES + 8 ; 8 + 8 = 16
|
||||||
|
cmp eax, HGCM_PL_BUF_SIZE
|
||||||
|
ja .buffer_overflow
|
||||||
|
|
||||||
|
; Заполняем PageListInfo с 1 фиктивной страницей
|
||||||
|
mov edi, [packet_base]
|
||||||
|
add edi, [data_tail]
|
||||||
|
mov eax, [conv_flags]
|
||||||
|
mov dword [edi + HGCM_PLI_FLAGS], eax
|
||||||
|
mov word [edi + HGCM_PLI_OFF_FIRST_PAGE], 0
|
||||||
|
mov word [edi + HGCM_PLI_CPAGES], 1 ; cPages=1 (не 0!)
|
||||||
|
mov dword [edi + HGCM_PLI_APAGES], 0 ; dummy phys lo
|
||||||
|
mov dword [edi + HGCM_PLI_APAGES + 4], 0 ; dummy phys hi
|
||||||
|
|
||||||
|
; Записываем параметр
|
||||||
|
mov dword [esi + HGCM_PARM.type], HGCM_PARM_TYPE_PAGELIST
|
||||||
|
mov dword [esi + HGCM_PARM.u], 0 ; cbData = 0
|
||||||
|
mov eax, [data_tail]
|
||||||
|
mov dword [esi + HGCM_PARM.u + 4], eax ; offset к PLI
|
||||||
|
|
||||||
|
; data_tail += 16 (8 заголовок + 8 одна страница)
|
||||||
|
add dword [data_tail], HGCM_PLI_APAGES + 8
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] parm[%d]: empty pagelist (cPages=1 dummy)\n", [param_idx]
|
||||||
|
jmp .next_param
|
||||||
|
|
||||||
|
.buffer_overflow:
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] ERROR: PageList overflow at param %d\n", [param_idx]
|
||||||
|
; mov dword [hgcm_pl_busy], 0
|
||||||
|
mov eax, VERR_TOO_MUCH_DATA
|
||||||
|
ret
|
||||||
|
|
||||||
|
.next_param:
|
||||||
|
inc dword [param_idx]
|
||||||
|
jmp .convert_loop
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 5. Обновляем размер пакета и отправляем
|
||||||
|
; =================================================================
|
||||||
|
.convert_done:
|
||||||
|
mov edi, [packet_base]
|
||||||
|
mov eax, [data_tail]
|
||||||
|
mov [edi + VMMDEV_HEADER.size], eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] Final packet size: %d bytes\n", eax
|
||||||
|
|
||||||
|
; Отправляем
|
||||||
|
stdcall hgcm_send_request, [packet_base]
|
||||||
|
|
||||||
|
; Debug post-send
|
||||||
|
push eax
|
||||||
|
mov esi, [packet_base]
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] POST: rc=0x%x result=0x%x\n", \
|
||||||
|
[esi + HGCM_HEADER.header.rc], [esi + HGCM_HEADER.result]
|
||||||
|
pop eax
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
jnz .send_failed
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 6. Copyback — только размеры и скалярные параметры
|
||||||
|
; Данные OUT/INOUT уже на месте в памяти гостя.
|
||||||
|
; =================================================================
|
||||||
|
mov dword [param_idx], 0
|
||||||
|
|
||||||
|
.cb_loop:
|
||||||
|
mov ecx, [param_idx]
|
||||||
|
cmp ecx, [parm_count]
|
||||||
|
jae .cb_done
|
||||||
|
|
||||||
|
; esi -> параметр в пакете
|
||||||
|
mov esi, [packet_base]
|
||||||
|
add esi, sizeof.HGCM_CALL
|
||||||
|
mov eax, ecx
|
||||||
|
imul eax, sizeof.HGCM_PARM
|
||||||
|
add esi, eax
|
||||||
|
|
||||||
|
; edi -> оригинальный параметр в parms[]
|
||||||
|
mov edi, [parms]
|
||||||
|
add edi, eax
|
||||||
|
|
||||||
|
mov eax, [esi + HGCM_PARM.type]
|
||||||
|
|
||||||
|
cmp eax, HGCM_PARM_TYPE_PAGELIST
|
||||||
|
je .cb_pagelist
|
||||||
|
cmp eax, HGCM_PARM_TYPE_32BIT
|
||||||
|
je .cb_32bit
|
||||||
|
cmp eax, HGCM_PARM_TYPE_64BIT
|
||||||
|
je .cb_64bit
|
||||||
|
jmp .cb_next
|
||||||
|
|
||||||
|
.cb_32bit:
|
||||||
|
; 32-bit значение — копируем обратно
|
||||||
|
mov eax, dword [esi + HGCM_PARM.u]
|
||||||
|
mov dword [edi + HGCM_PARM.u], eax
|
||||||
|
jmp .cb_next
|
||||||
|
|
||||||
|
.cb_64bit:
|
||||||
|
; 64-bit значение — копируем обратно
|
||||||
|
mov eax, dword [esi + HGCM_PARM.u]
|
||||||
|
mov dword [edi + HGCM_PARM.u], eax
|
||||||
|
mov eax, dword [esi + HGCM_PARM.u + 4]
|
||||||
|
mov dword [edi + HGCM_PARM.u + 4], eax
|
||||||
|
jmp .cb_next
|
||||||
|
|
||||||
|
.cb_pagelist:
|
||||||
|
; Для PAGE_LIST: хост мог обновить cbData (u[0..3]).
|
||||||
|
; Копируем обновлённый размер в оригинальный параметр (LinAddr.size).
|
||||||
|
; Адрес буфера (LinAddr.offset) не меняется.
|
||||||
|
mov eax, dword [esi + HGCM_PARM.u]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] cb[%d]: size=%d\n", ecx, eax
|
||||||
|
jmp .cb_next
|
||||||
|
|
||||||
|
.cb_next:
|
||||||
|
inc dword [param_idx]
|
||||||
|
jmp .cb_loop
|
||||||
|
|
||||||
|
.cb_done:
|
||||||
|
xor eax, eax
|
||||||
|
.send_failed:
|
||||||
|
; mov dword [hgcm_pl_busy], 0
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad:
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] [PageList] ERROR: hgcm_call_pl_virt is NULL!\n"
|
||||||
|
; mov dword [hgcm_pl_busy], 0
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
|
||||||
|
.busy:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [HGCM] [PageList] BUSY — reentrancy blocked\n"
|
||||||
|
mov eax, VERR_TRY_AGAIN
|
||||||
|
ret
|
||||||
|
|
||||||
|
.send_no_params:
|
||||||
|
; Без параметров — размер и отправка как в .convert_done
|
||||||
|
jmp .convert_done
|
||||||
|
|
||||||
|
endp
|
||||||
5
hgcm/core.inc
Normal file
5
hgcm/core.inc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
; HGCM подсистема
|
||||||
|
include 'hgcm/async.inc'
|
||||||
|
include 'hgcm/params.inc'
|
||||||
|
include 'hgcm/hgcm.inc'
|
||||||
|
include 'hgcm/call.inc'
|
||||||
276
hgcm/hgcm.inc
Normal file
276
hgcm/hgcm.inc
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : HGCM Core
|
||||||
|
; Назначение : HGCM: init packets + connect/disconnect + общий send_request
|
||||||
|
; Файл : hgcm/hgcm.inc
|
||||||
|
; =============================================================================
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Макрос: hgcm_prepare_header
|
||||||
|
; Назначение: заполнить VMMDevRequestHeader внутри HGCM request
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
macro hgcm_prepare_header req_ptr, req_size, req_type
|
||||||
|
{
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.header.size], req_size
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.header.request_type], req_type
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.header.rc], VERR_GENERAL_FAILURE ; 0 ;VERR_NOT_READY
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.header.reserved1], 0
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.header.f_requestor], VMMDEV_REQUESTOR_VBOXGUEST
|
||||||
|
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.flags], 0
|
||||||
|
mov dword [req_ptr + HGCM_HEADER.result], 0
|
||||||
|
}
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_init_packets — выделить и привязать буферы HGCM (KernelAlloc)
|
||||||
|
;
|
||||||
|
; Выделяем:
|
||||||
|
; 1 страница (4096) для connect + disconnect (помещаются вместе)
|
||||||
|
; 1 страница (4096) для hgcm_call_s (embedded)
|
||||||
|
; N страниц для hgcm_call_pl_s (pagelist)
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_init_packets uses ebx
|
||||||
|
|
||||||
|
; --- Страница для connect + disconnect ---
|
||||||
|
invoke KernelAlloc, PAGE_SIZE
|
||||||
|
test eax, eax
|
||||||
|
jz .alloc_fail
|
||||||
|
|
||||||
|
; connect = начало страницы
|
||||||
|
mov [vbox_device.hgcm_connect_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.hgcm_connect_phys], eax
|
||||||
|
|
||||||
|
; disconnect = connect_virt + sizeof.HGCM_CONNECT (выровнено на 4)
|
||||||
|
mov eax, [vbox_device.hgcm_connect_virt]
|
||||||
|
add eax, ((sizeof.HGCM_CONNECT + 3) and (not 3))
|
||||||
|
mov [vbox_device.hgcm_disconnect_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.hgcm_disconnect_phys], eax
|
||||||
|
|
||||||
|
; --- Страница для embedded call ---
|
||||||
|
invoke KernelAlloc, PAGE_SIZE
|
||||||
|
test eax, eax
|
||||||
|
jz .alloc_fail_free1
|
||||||
|
|
||||||
|
mov [vbox_device.hgcm_call_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.hgcm_call_phys], eax
|
||||||
|
|
||||||
|
; --- Буфер для pagelist call
|
||||||
|
invoke KernelAlloc, HGCM_PL_BUF_SIZE
|
||||||
|
test eax, eax
|
||||||
|
jz .alloc_fail_free2
|
||||||
|
|
||||||
|
mov [vbox_device.hgcm_call_pl_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.hgcm_call_pl_phys], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [HGCM] Packets allocated: connect=0x%x call=0x%x pl=0x%x\n", \
|
||||||
|
[vbox_device.hgcm_connect_virt], [vbox_device.hgcm_call_virt], [vbox_device.hgcm_call_pl_virt]
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.alloc_fail_free2:
|
||||||
|
invoke KernelFree, [vbox_device.hgcm_call_virt]
|
||||||
|
.alloc_fail_free1:
|
||||||
|
invoke KernelFree, [vbox_device.hgcm_connect_virt]
|
||||||
|
.alloc_fail:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [HGCM] ERROR: KernelAlloc failed for HGCM packets\n"
|
||||||
|
mov eax, VERR_NO_MEMORY
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_free_packets — освободить буферы HGCM
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_free_packets
|
||||||
|
; Pagelist call buffer
|
||||||
|
cmp dword [vbox_device.hgcm_call_pl_virt], 0
|
||||||
|
je @f
|
||||||
|
invoke KernelFree, [vbox_device.hgcm_call_pl_virt]
|
||||||
|
mov dword [vbox_device.hgcm_call_pl_virt], 0
|
||||||
|
@@:
|
||||||
|
; Embedded call buffer
|
||||||
|
cmp dword [vbox_device.hgcm_call_virt], 0
|
||||||
|
je @f
|
||||||
|
invoke KernelFree, [vbox_device.hgcm_call_virt]
|
||||||
|
mov dword [vbox_device.hgcm_call_virt], 0
|
||||||
|
@@:
|
||||||
|
; Connect+disconnect page
|
||||||
|
cmp dword [vbox_device.hgcm_connect_virt], 0
|
||||||
|
je @f
|
||||||
|
invoke KernelFree, [vbox_device.hgcm_connect_virt]
|
||||||
|
mov dword [vbox_device.hgcm_connect_virt], 0
|
||||||
|
mov dword [vbox_device.hgcm_disconnect_virt], 0
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_init инициализация HGCM подсистемы
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_init
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Initializing HGCM subsystem\n"
|
||||||
|
|
||||||
|
; Инициализируем пакеты
|
||||||
|
call hgcm_init_packets
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Initialization complete\n"
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; hgcm_send_request — отправить HGCM request через VMMDev + обработать ASYNC
|
||||||
|
proc hgcm_send_request uses ebx ecx edx esi edi, request_ptr:dword
|
||||||
|
mov esi, [request_ptr]
|
||||||
|
test esi, esi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Sending request type=0x%x\n", [esi + HGCM_HEADER.header.request_type]
|
||||||
|
|
||||||
|
; phys адрес запроса
|
||||||
|
mov eax, esi
|
||||||
|
invoke GetPhysAddr
|
||||||
|
test eax, eax
|
||||||
|
jz .phys_failed
|
||||||
|
mov edx, eax
|
||||||
|
|
||||||
|
; отправка VMMDev (submit-only, хост обновляет пакет через MMIO)
|
||||||
|
stdcall vmmdev_send_request, edx
|
||||||
|
|
||||||
|
; транспортный rc
|
||||||
|
mov eax, [esi + HGCM_HEADER.header.rc]
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Transport RC: 0x%x\n", eax
|
||||||
|
|
||||||
|
cmp eax, VINF_HGCM_ASYNC_EXECUTE
|
||||||
|
je .async
|
||||||
|
|
||||||
|
; если rc < 0 — ошибка транспорта
|
||||||
|
test eax, eax
|
||||||
|
js .transport_error
|
||||||
|
|
||||||
|
; sync ok -> проверяем service result
|
||||||
|
jmp .check_service
|
||||||
|
|
||||||
|
.async:
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Request is async, waiting...\n"
|
||||||
|
stdcall hgcm_wait_async, esi
|
||||||
|
test eax, eax
|
||||||
|
jnz .async_failed
|
||||||
|
|
||||||
|
; после async: транспортный rc
|
||||||
|
mov eax, [esi + HGCM_HEADER.header.rc]
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Post-async transport RC: 0x%x\n", eax
|
||||||
|
test eax, eax
|
||||||
|
js .transport_error_after_async
|
||||||
|
|
||||||
|
.check_service:
|
||||||
|
; результат сервиса
|
||||||
|
mov eax, [esi + HGCM_HEADER.result]
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Service result: 0x%x\n", eax
|
||||||
|
test eax, eax
|
||||||
|
js .service_error
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
|
||||||
|
.phys_failed:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
|
||||||
|
.transport_error:
|
||||||
|
ret
|
||||||
|
|
||||||
|
.transport_error_after_async:
|
||||||
|
ret
|
||||||
|
|
||||||
|
.service_error:
|
||||||
|
ret
|
||||||
|
|
||||||
|
.async_failed:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; hgcm_connect — подключение к HGCM сервису
|
||||||
|
proc hgcm_connect uses ebx ecx edx esi edi, service_name:dword
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Connecting to service: %s\n", [service_name]
|
||||||
|
|
||||||
|
mov edi, [vbox_device.hgcm_connect_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .failed
|
||||||
|
|
||||||
|
; header
|
||||||
|
hgcm_prepare_header edi, sizeof.HGCM_CONNECT, VMMDEV_HGCM_CONNECT
|
||||||
|
|
||||||
|
; location
|
||||||
|
mov dword [edi + HGCM_CONNECT.location_type], HGCM_LOC_TYPE_PREDEFINED
|
||||||
|
|
||||||
|
; очистить service_name буфер
|
||||||
|
push edi
|
||||||
|
lea edi, [edi + HGCM_CONNECT.service_name]
|
||||||
|
xor eax, eax
|
||||||
|
mov ecx, HGCM_SERVICE_NAME_MAX / 4
|
||||||
|
rep stosd
|
||||||
|
pop edi
|
||||||
|
|
||||||
|
; копировать ASCII имя сервиса
|
||||||
|
mov esi, [service_name]
|
||||||
|
push edi
|
||||||
|
lea edi, [edi + HGCM_CONNECT.service_name]
|
||||||
|
mov ecx, HGCM_SERVICE_NAME_MAX - 1
|
||||||
|
.copy_name:
|
||||||
|
lodsb
|
||||||
|
test al, al
|
||||||
|
jz .name_done
|
||||||
|
stosb
|
||||||
|
loop .copy_name
|
||||||
|
.name_done:
|
||||||
|
xor al, al
|
||||||
|
stosb
|
||||||
|
pop edi
|
||||||
|
|
||||||
|
; client_id = 0
|
||||||
|
mov dword [edi + HGCM_CONNECT.client_id], 0
|
||||||
|
|
||||||
|
; отправить
|
||||||
|
stdcall hgcm_send_request, [vbox_device.hgcm_connect_virt]
|
||||||
|
test eax, eax
|
||||||
|
jnz .failed
|
||||||
|
|
||||||
|
mov eax, [edi + HGCM_CONNECT.client_id]
|
||||||
|
test eax, eax
|
||||||
|
jz .failed
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Connected, client_id=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [HGCM] Connect failed\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; hgcm_disconnect — отключение клиента HGCM
|
||||||
|
proc hgcm_disconnect uses edi, client_id:dword
|
||||||
|
DEBUGF __DEBUG_HGCM__, "[VBoxGuest] [HGCM] Disconnecting client: 0x%x\n", [client_id]
|
||||||
|
|
||||||
|
mov edi, [vbox_device.hgcm_disconnect_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad
|
||||||
|
|
||||||
|
hgcm_prepare_header edi, sizeof.HGCM_DISCONNECT, VMMDEV_HGCM_DISCONNECT
|
||||||
|
|
||||||
|
mov eax, [client_id]
|
||||||
|
mov [edi + HGCM_DISCONNECT.client_id], eax
|
||||||
|
|
||||||
|
stdcall hgcm_send_request, [vbox_device.hgcm_disconnect_virt]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
78
hgcm/params.inc
Normal file
78
hgcm/params.inc
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : HGCM Params
|
||||||
|
; Назначение : Формирование параметров HGCM_PARM
|
||||||
|
; Файл : hgcm/params.inc
|
||||||
|
; Версия : 1.1
|
||||||
|
; Дата : 2025.01.15
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; hgcm_param_32 — заполнить 32-битный параметр
|
||||||
|
;
|
||||||
|
; Вход : p — указатель на HGCM_PARM, value — значение
|
||||||
|
proc hgcm_param_32 uses edi, p:dword, value:dword
|
||||||
|
mov edi, [p]
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
||||||
|
mov eax, [value]
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_param_64 — заполнить 64-битный параметр
|
||||||
|
;
|
||||||
|
; Вход : p — указатель на HGCM_PARM, lo/hi — младшая/старшая часть
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_param_64 uses edi, p:dword, lo:dword, hi:dword
|
||||||
|
mov edi, [p]
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_64BIT
|
||||||
|
mov eax, [lo]
|
||||||
|
mov dword [edi + HGCM_PARM.u.value64_lo], eax
|
||||||
|
mov eax, [hi]
|
||||||
|
mov dword [edi + HGCM_PARM.u.value64_hi], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_param_linaddr_in — линейный буфер IN (гость → хост)
|
||||||
|
;
|
||||||
|
; Вход : p — указатель на HGCM_PARM, addr — адрес буфера, size — размер
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_param_linaddr_in uses edi, p:dword, addr:dword, size:dword
|
||||||
|
mov edi, [p]
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_IN
|
||||||
|
mov eax, [size]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
|
||||||
|
mov eax, [addr]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_param_linaddr_out — линейный буфер OUT (хост → гость)
|
||||||
|
;
|
||||||
|
; Вход : p — указатель на HGCM_PARM, addr — адрес буфера, size — размер
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_param_linaddr_out uses edi, p:dword, addr:dword, size:dword
|
||||||
|
mov edi, [p]
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_OUT
|
||||||
|
mov eax, [size]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
|
||||||
|
mov eax, [addr]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; hgcm_param_linaddr_inout — линейный буфер INOUT (двунаправленный)
|
||||||
|
;
|
||||||
|
; Вход : p — указатель на HGCM_PARM, addr — адрес буфера, size — размер
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
proc hgcm_param_linaddr_inout uses edi, p:dword, addr:dword, size:dword
|
||||||
|
mov edi, [p]
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_INOUT
|
||||||
|
mov eax, [size]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
|
||||||
|
mov eax, [addr]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
@@ -1,929 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; Clipboard Service - ИСПРАВЛЕННАЯ ВЕРСИЯ
|
|
||||||
;
|
|
||||||
; Ключевые изменения по сравнению с оригиналом:
|
|
||||||
; 1. FSM (Finite State Machine): INIT → CONNECTING → READY
|
|
||||||
; 2. UTF-8 ↔ UTF-16LE конвертация (как в Linux драйвере)
|
|
||||||
; 3. Правильная обработка VERR_RESOURCE_BUSY
|
|
||||||
; 4. Проверки готовности данных перед чтением
|
|
||||||
; 5. НЕТ HGCM вызовов до state == READY
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
align 4
|
|
||||||
clipboard_state CLIPBOARD_STATE
|
|
||||||
|
|
||||||
clipboard_service_name db 'VBoxSharedClipboard', 0
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: clipboard_init
|
|
||||||
; Инициализация clipboard с FSM
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_init uses ebx ecx edx esi edi
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
DEBUGF 1, "[clip] >>> clipboard_init STARTED\n"
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
|
|
||||||
; Инициализация FSM state
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
|
|
||||||
|
|
||||||
mov ax, [vbox_device.port]
|
|
||||||
test ax, ax
|
|
||||||
jz .vmmdev_not_ready
|
|
||||||
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .vmmdev_not_ready
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] VMMDev ready: port=0x%x, mmio=0x%x\n", \
|
|
||||||
[vbox_device.port], [vbox_device.mmio]
|
|
||||||
|
|
||||||
cmp dword [clipboard_state.connected], 1
|
|
||||||
je .already_connected
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Not connected yet, initializing...\n"
|
|
||||||
DEBUGF 2, "[clip] Step 1/6: Setting state to CONNECTING...\n"
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; FSM: INIT → CONNECTING
|
|
||||||
; =========================================================================
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_CONNECTING
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Step 2/6: Connecting to HGCM service...\n"
|
|
||||||
stdcall hgcm_connect, clipboard_service_name
|
|
||||||
test eax, eax
|
|
||||||
jz .connect_failed
|
|
||||||
|
|
||||||
mov [clipboard_state.client_id], eax
|
|
||||||
mov dword [clipboard_state.connected], 1
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] *** HGCM CONNECTED *** client_id=0x%x\n", eax
|
|
||||||
DEBUGF 2, "[clip] Step 3/6: Allocating main buffer (%d bytes)...\n", \
|
|
||||||
VBOX_SHCL_MAX_CHUNK_SIZE
|
|
||||||
|
|
||||||
invoke KernelAlloc, VBOX_SHCL_MAX_CHUNK_SIZE
|
|
||||||
test eax, eax
|
|
||||||
jz .alloc_failed
|
|
||||||
|
|
||||||
mov [clipboard_state.buffer_virt], eax
|
|
||||||
mov dword [clipboard_state.buffer_size], VBOX_SHCL_MAX_CHUNK_SIZE
|
|
||||||
mov dword [clipboard_state.last_data_size], 0
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Main buffer allocated: virt=0x%x, size=%d\n", \
|
|
||||||
eax, VBOX_SHCL_MAX_CHUNK_SIZE
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; Выделяем UTF-16 буфер (в 2 раза больше для worst case)
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 2, "[clip] Step 4/6: Allocating UTF-16 buffer...\n"
|
|
||||||
|
|
||||||
invoke KernelAlloc, VBOX_SHCL_MAX_CHUNK_SIZE * 2
|
|
||||||
test eax, eax
|
|
||||||
jz .alloc_utf16_failed
|
|
||||||
|
|
||||||
mov [clipboard_state.utf16_buffer], eax
|
|
||||||
mov dword [clipboard_state.utf16_size], VBOX_SHCL_MAX_CHUNK_SIZE * 2
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] UTF-16 buffer allocated: virt=0x%x, size=%d\n", \
|
|
||||||
eax, [clipboard_state.utf16_size]
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Step 5/6: Getting physical address...\n"
|
|
||||||
mov eax, [clipboard_state.buffer_virt]
|
|
||||||
invoke GetPhysAddr
|
|
||||||
test eax, eax
|
|
||||||
jz .phys_failed
|
|
||||||
|
|
||||||
mov [clipboard_state.buffer_phys], eax
|
|
||||||
DEBUGF 2, "[clip] Buffer phys=0x%x\n", eax
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Step 6/6: Initializing HGCM packets...\n"
|
|
||||||
call clipboard_init_packets
|
|
||||||
DEBUGF 2, "[clip] Packets initialized\n"
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; Сообщаем что у нас НЕТ данных при инициализации
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 2, "[clip] Reporting initial formats (none)...\n"
|
|
||||||
stdcall clipboard_report_formats, 0
|
|
||||||
test eax, eax
|
|
||||||
jnz .report_failed
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; FSM: CONNECTING → READY
|
|
||||||
; С ЭТОГО МОМЕНТА IRQ может обрабатывать clipboard события
|
|
||||||
; =========================================================================
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_READY
|
|
||||||
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
DEBUGF 1, "[clip] *** CLIPBOARD READY ***\n"
|
|
||||||
DEBUGF 1, "[clip] FSM state : READY\n"
|
|
||||||
DEBUGF 1, "[clip] client_id : 0x%x\n", [clipboard_state.client_id]
|
|
||||||
DEBUGF 1, "[clip] buffer_virt : 0x%x\n", [clipboard_state.buffer_virt]
|
|
||||||
DEBUGF 1, "[clip] buffer_phys : 0x%x\n", [clipboard_state.buffer_phys]
|
|
||||||
DEBUGF 1, "[clip] buffer_size : %d bytes\n", [clipboard_state.buffer_size]
|
|
||||||
DEBUGF 1, "[clip] utf16_buffer : 0x%x\n", [clipboard_state.utf16_buffer]
|
|
||||||
DEBUGF 1, "[clip] utf16_size : %d bytes\n", [clipboard_state.utf16_size]
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.vmmdev_not_ready:
|
|
||||||
DEBUGF 1, "[clip] ERROR: VMMDev not initialized!\n"
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
|
|
||||||
mov eax, VERR_GENERAL_FAILURE
|
|
||||||
ret
|
|
||||||
|
|
||||||
.already_connected:
|
|
||||||
DEBUGF 2, "[clip] Already connected (client_id=0x%x, state=%d)\n", \
|
|
||||||
[clipboard_state.client_id], [clipboard_state.state]
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.connect_failed:
|
|
||||||
DEBUGF 1, "[clip] *** CRITICAL: HGCM connect failed ***\n"
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
|
|
||||||
mov eax, VERR_HGCM_SERVICE_NOT_FOUND
|
|
||||||
ret
|
|
||||||
|
|
||||||
.alloc_failed:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Main buffer alloc failed\n"
|
|
||||||
stdcall hgcm_disconnect, [clipboard_state.client_id]
|
|
||||||
mov dword [clipboard_state.connected], 0
|
|
||||||
mov dword [clipboard_state.client_id], 0
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
|
|
||||||
mov eax, VERR_NO_MEMORY
|
|
||||||
ret
|
|
||||||
|
|
||||||
.alloc_utf16_failed:
|
|
||||||
DEBUGF 1, "[clip] ERROR: UTF-16 buffer alloc failed\n"
|
|
||||||
invoke KernelFree, [clipboard_state.buffer_virt]
|
|
||||||
stdcall hgcm_disconnect, [clipboard_state.client_id]
|
|
||||||
mov dword [clipboard_state.connected], 0
|
|
||||||
mov dword [clipboard_state.client_id], 0
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
|
|
||||||
mov eax, VERR_NO_MEMORY
|
|
||||||
ret
|
|
||||||
|
|
||||||
.phys_failed:
|
|
||||||
DEBUGF 1, "[clip] ERROR: GetPhysAddr failed\n"
|
|
||||||
invoke KernelFree, [clipboard_state.utf16_buffer]
|
|
||||||
invoke KernelFree, [clipboard_state.buffer_virt]
|
|
||||||
stdcall hgcm_disconnect, [clipboard_state.client_id]
|
|
||||||
mov dword [clipboard_state.connected], 0
|
|
||||||
mov dword [clipboard_state.client_id], 0
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
|
|
||||||
mov eax, VERR_GENERAL_FAILURE
|
|
||||||
ret
|
|
||||||
|
|
||||||
.report_failed:
|
|
||||||
DEBUGF 1, "[clip] WARNING: report_formats failed (non-critical)\n"
|
|
||||||
; НЕ сбрасываем state - это не критично
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: clipboard_init_packets
|
|
||||||
; Без изменений - как в оригинале
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_init_packets uses eax ebx ecx edi
|
|
||||||
DEBUGF 2, "[clip] >>> clipboard_init_packets\n"
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.client_id]
|
|
||||||
test eax, eax
|
|
||||||
jz .error_no_client
|
|
||||||
|
|
||||||
; MSG_GET packet
|
|
||||||
mov edi, shcl_msg_get
|
|
||||||
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_MSG_GET
|
|
||||||
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
|
|
||||||
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved1], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved2], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.flags], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.client_id]
|
|
||||||
mov [edi + SHCL_MSG_GET.header.client_id], eax
|
|
||||||
mov dword [edi + SHCL_MSG_GET.header.function], VBOX_SHCL_GUEST_FN_MSG_GET
|
|
||||||
mov dword [edi + SHCL_MSG_GET.header.param_count], 2
|
|
||||||
|
|
||||||
hgcm_set_parm_u32 edi + SHCL_MSG_GET.msg_type, 0
|
|
||||||
hgcm_set_parm_u32 edi + SHCL_MSG_GET.formats, 0
|
|
||||||
|
|
||||||
; FORMATS_REPORT packet
|
|
||||||
mov edi, shcl_formats_report
|
|
||||||
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_FORMATS_REPORT
|
|
||||||
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
|
|
||||||
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved1], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved2], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.flags], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.client_id]
|
|
||||||
mov [edi + SHCL_FORMATS_REPORT.header.client_id], eax
|
|
||||||
mov dword [edi + SHCL_FORMATS_REPORT.header.function], VBOX_SHCL_GUEST_FN_FORMATS_REPORT
|
|
||||||
mov dword [edi + SHCL_FORMATS_REPORT.header.param_count], 1
|
|
||||||
|
|
||||||
hgcm_set_parm_u32 edi + SHCL_FORMATS_REPORT.formats, 0
|
|
||||||
|
|
||||||
; DATA_READ packet
|
|
||||||
mov edi, shcl_data_read
|
|
||||||
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_DATA_READ
|
|
||||||
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
|
|
||||||
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved1], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved2], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.flags], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.client_id]
|
|
||||||
mov [edi + SHCL_DATA_READ.header.client_id], eax
|
|
||||||
mov dword [edi + SHCL_DATA_READ.header.function], VBOX_SHCL_GUEST_FN_DATA_READ
|
|
||||||
mov dword [edi + SHCL_DATA_READ.header.param_count], 3
|
|
||||||
|
|
||||||
hgcm_set_parm_u32 edi + SHCL_DATA_READ.format, 0
|
|
||||||
hgcm_set_parm_ptr edi + SHCL_DATA_READ.buffer, 0, 0
|
|
||||||
hgcm_set_parm_u32 edi + SHCL_DATA_READ.size, 0
|
|
||||||
|
|
||||||
; DATA_WRITE packet
|
|
||||||
mov edi, shcl_data_write
|
|
||||||
mov dword [edi + HGCM_HEADER.header.size], sizeof.SHCL_DATA_WRITE
|
|
||||||
mov dword [edi + HGCM_HEADER.header.version], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [edi + HGCM_HEADER.header.request_type], VMMDEV_HGCM_CALL32
|
|
||||||
mov dword [edi + HGCM_HEADER.header.rc], VERR_INTERNAL_ERROR
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved1], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.header.reserved2], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.flags], 0
|
|
||||||
mov dword [edi + HGCM_HEADER.result], VERR_INTERNAL_ERROR
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.client_id]
|
|
||||||
mov [edi + SHCL_DATA_WRITE.header.client_id], eax
|
|
||||||
mov dword [edi + SHCL_DATA_WRITE.header.function], VBOX_SHCL_GUEST_FN_DATA_WRITE
|
|
||||||
mov dword [edi + SHCL_DATA_WRITE.header.param_count], 2
|
|
||||||
|
|
||||||
hgcm_set_parm_u32 edi + SHCL_DATA_WRITE.format, 0
|
|
||||||
hgcm_set_parm_ptr edi + SHCL_DATA_WRITE.buffer, 0, 0
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error_no_client:
|
|
||||||
DEBUGF 1, "[clip] ERROR: No client_id for packet init\n"
|
|
||||||
; mov eax, VERR_INVALID_STATE
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: clipboard_get_host_msg
|
|
||||||
; ИСПРАВЛЕНО: Правильная обработка VERR_RESOURCE_BUSY
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_get_host_msg uses ebx esi edi
|
|
||||||
DEBUGF 2, "[clip] >>> clipboard_get_host_msg\n"
|
|
||||||
|
|
||||||
; Проверка FSM state
|
|
||||||
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
|
|
||||||
jne .not_ready
|
|
||||||
|
|
||||||
mov esi, shcl_msg_get
|
|
||||||
test esi, esi
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
; Reset output parameters
|
|
||||||
lea edi, [esi + SHCL_MSG_GET.msg_type]
|
|
||||||
hgcm_set_parm_u32 edi, 0
|
|
||||||
|
|
||||||
lea edi, [esi + SHCL_MSG_GET.formats]
|
|
||||||
hgcm_set_parm_u32 edi, 0
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.buffer_phys]
|
|
||||||
stdcall hgcm_send_request, esi, eax
|
|
||||||
test eax, eax
|
|
||||||
jnz .hgcm_error
|
|
||||||
|
|
||||||
; Получаем результаты
|
|
||||||
mov eax, [esi + HGCM_HEADER.result]
|
|
||||||
test eax, eax
|
|
||||||
jnz .check_interrupted
|
|
||||||
|
|
||||||
mov eax, [esi + SHCL_MSG_GET.msg_type + HGCM_PARM.value_or_size]
|
|
||||||
mov ebx, [esi + SHCL_MSG_GET.formats + HGCM_PARM.value_or_size]
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Got message: type=%d, formats=0x%x\n", eax, ebx
|
|
||||||
ret
|
|
||||||
|
|
||||||
.check_interrupted:
|
|
||||||
; =========================================================================
|
|
||||||
; ИСПРАВЛЕНО: Правильная обработка VERR_RESOURCE_BUSY (0xFFFFFF76 -138)
|
|
||||||
; Это означает что сервис сбросился или изменил состояние
|
|
||||||
; =========================================================================
|
|
||||||
cmp eax, 0xFFFFFF76 ; VERR_RESOURCE_BUSY
|
|
||||||
jne .check_invalid_param
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] VERR_RESOURCE_BUSY - service state changed\n"
|
|
||||||
|
|
||||||
; Сброс форматов при INTERRUPTED
|
|
||||||
mov dword [clipboard_state.formats_host], 0
|
|
||||||
mov dword [clipboard_state.formats_guest], 0
|
|
||||||
|
|
||||||
xor eax, eax ; Возвращаем "нет сообщения"
|
|
||||||
xor ebx, ebx
|
|
||||||
ret
|
|
||||||
|
|
||||||
.check_invalid_param:
|
|
||||||
cmp eax, 0xFFFFFFE7 ; VERR_INVALID_PARAMETER
|
|
||||||
jne .check_other_errors
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] VERR_INVALID_PARAMETER\n"
|
|
||||||
jmp .error
|
|
||||||
|
|
||||||
.check_other_errors:
|
|
||||||
; Другие ошибки
|
|
||||||
DEBUGF 1, "[clip] GET_HOST_MSG error: 0x%x\n", eax
|
|
||||||
jmp .error
|
|
||||||
|
|
||||||
.not_ready:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Clipboard not ready (state=%d)\n", \
|
|
||||||
[clipboard_state.state]
|
|
||||||
; mov eax, VERR_INVALID_STATE
|
|
||||||
ret
|
|
||||||
|
|
||||||
.hgcm_error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
mov eax, VERR_GENERAL_FAILURE
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Clipboard Service - ИСПРАВЛЕННАЯ ВЕРСИЯ (ЧАСТЬ 2)
|
|
||||||
; Функции чтения/записи с UTF-8 ↔ UTF-16LE конвертацией
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: clipboard_read_data
|
|
||||||
; ИСПРАВЛЕНО: Добавлена конвертация UTF-16LE → UTF-8
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_read_data uses ebx esi edi, format:dword, buffer:dword, buffer_size:dword
|
|
||||||
DEBUGF 1, "[clip] >>> clipboard_read_data(format=0x%x, buf=0x%x, size=%d)\n", \
|
|
||||||
[format], [buffer], [buffer_size]
|
|
||||||
|
|
||||||
; Проверка FSM state
|
|
||||||
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
|
|
||||||
jne .not_ready
|
|
||||||
|
|
||||||
mov eax, [buffer]
|
|
||||||
test eax, eax
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
mov ecx, [buffer_size]
|
|
||||||
test ecx, ecx
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
; Проверяем формат
|
|
||||||
mov eax, [format]
|
|
||||||
test eax, eax
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; Читаем в UTF-16 буфер сначала (VirtualBox отдает UTF-16LE)
|
|
||||||
; =========================================================================
|
|
||||||
mov esi, shcl_data_read
|
|
||||||
test esi, esi
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
lea edi, [esi + SHCL_DATA_READ.format]
|
|
||||||
mov eax, [format]
|
|
||||||
hgcm_set_parm_u32 edi, eax
|
|
||||||
|
|
||||||
lea edi, [esi + SHCL_DATA_READ.buffer]
|
|
||||||
mov eax, [clipboard_state.utf16_buffer] ; Читаем в UTF-16 буфер!
|
|
||||||
mov ebx, [clipboard_state.buffer_phys]
|
|
||||||
hgcm_set_parm_ptr edi, eax, ebx
|
|
||||||
|
|
||||||
lea edi, [esi + SHCL_DATA_READ.size]
|
|
||||||
mov eax, [clipboard_state.utf16_size]
|
|
||||||
hgcm_set_parm_u32 edi, eax
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Reading to UTF-16 buffer: virt=0x%x, phys=0x%x, max=%d\n", \
|
|
||||||
[clipboard_state.utf16_buffer], [clipboard_state.buffer_phys], \
|
|
||||||
[clipboard_state.utf16_size]
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.buffer_phys]
|
|
||||||
stdcall hgcm_send_request, esi, eax
|
|
||||||
test eax, eax
|
|
||||||
jnz .hgcm_error
|
|
||||||
|
|
||||||
; Проверяем результат
|
|
||||||
mov eax, [esi + HGCM_HEADER.result]
|
|
||||||
test eax, eax
|
|
||||||
jnz .read_failed
|
|
||||||
|
|
||||||
; Получаем реальный размер прочитанных данных (UTF-16LE)
|
|
||||||
mov eax, [esi + SHCL_DATA_READ.size + HGCM_PARM.value_or_size]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_data
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Read %d bytes (UTF-16LE with BOM)\n", eax
|
|
||||||
|
|
||||||
; Сохраняем размер UTF-16 данных
|
|
||||||
push eax ; Размер UTF-16
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; Конвертируем UTF-16LE → UTF-8
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 2, "[clip] Converting UTF-16LE → UTF-8...\n"
|
|
||||||
|
|
||||||
stdcall utf16le_to_utf8, \
|
|
||||||
[clipboard_state.utf16_buffer], \ ; src (UTF-16LE)
|
|
||||||
eax, \ ; src_len
|
|
||||||
[buffer], \ ; dst (UTF-8)
|
|
||||||
[buffer_size] ; dst_max
|
|
||||||
|
|
||||||
pop ebx ; Восстанавливаем UTF-16 size
|
|
||||||
|
|
||||||
cmp eax, -1
|
|
||||||
je .conversion_failed
|
|
||||||
|
|
||||||
; EAX = размер UTF-8 данных (без null)
|
|
||||||
DEBUGF 1, "[clip] Conversion OK: %d bytes UTF-16LE → %d bytes UTF-8\n", ebx, eax
|
|
||||||
|
|
||||||
; Сохраняем размер для последующего использования
|
|
||||||
mov [clipboard_state.last_data_size], eax
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
.no_data:
|
|
||||||
DEBUGF 2, "[clip] No data read (size=0)\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.read_failed:
|
|
||||||
DEBUGF 1, "[clip] READ_DATA failed: rc=0x%x\n", eax
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.conversion_failed:
|
|
||||||
DEBUGF 1, "[clip] ERROR: UTF-16LE → UTF-8 conversion failed\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.not_ready:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Clipboard not ready\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.invalid_param:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Invalid parameters\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.hgcm_error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Internal error\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: clipboard_write_data
|
|
||||||
; ИСПРАВЛЕНО: Добавлена конвертация UTF-8 → UTF-16LE
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_write_data uses ebx esi edi, format:dword, buffer:dword, size:dword
|
|
||||||
DEBUGF 1, "[clip] >>> clipboard_write_data(format=0x%x, buf=0x%x, size=%d)\n", \
|
|
||||||
[format], [buffer], [size]
|
|
||||||
|
|
||||||
; Проверка FSM state
|
|
||||||
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
|
|
||||||
jne .not_ready
|
|
||||||
|
|
||||||
mov eax, [buffer]
|
|
||||||
test eax, eax
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
mov ecx, [size]
|
|
||||||
test ecx, ecx
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; Конвертируем UTF-8 → UTF-16LE перед отправкой
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 2, "[clip] Converting UTF-8 → UTF-16LE...\n"
|
|
||||||
|
|
||||||
; stdcall utf8_to_utf16le, \
|
|
||||||
; [buffer], \ ; src (UTF-8)
|
|
||||||
; [size], \ ; src_len
|
|
||||||
; [clipboard_state.utf16_buffer], \ ; dst (UTF-16LE)
|
|
||||||
; [clipboard_state.utf16_size] ; dst_max
|
|
||||||
|
|
||||||
cmp eax, -1
|
|
||||||
je .conversion_failed
|
|
||||||
|
|
||||||
; EAX = размер UTF-16LE данных (с BOM)
|
|
||||||
push eax ; Сохраняем для отчета
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Conversion OK: %d bytes UTF-8 → %d bytes UTF-16LE (with BOM)\n", \
|
|
||||||
[size], eax
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; Отправляем UTF-16LE данные в VirtualBox
|
|
||||||
; =========================================================================
|
|
||||||
mov esi, shcl_data_write
|
|
||||||
test esi, esi
|
|
||||||
jz .error_cleanup
|
|
||||||
|
|
||||||
lea edi, [esi + SHCL_DATA_WRITE.format]
|
|
||||||
mov eax, [format]
|
|
||||||
hgcm_set_parm_u32 edi, eax
|
|
||||||
|
|
||||||
lea edi, [esi + SHCL_DATA_WRITE.buffer]
|
|
||||||
mov eax, [clipboard_state.utf16_buffer] ; Отправляем из UTF-16 буфера!
|
|
||||||
mov ebx, [clipboard_state.buffer_phys]
|
|
||||||
pop ecx ; Размер UTF-16
|
|
||||||
hgcm_set_parm_ptr edi, eax, ecx
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Writing UTF-16LE: virt=0x%x, phys=0x%x, size=%d\n", \
|
|
||||||
eax, ebx, ecx
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.buffer_phys]
|
|
||||||
stdcall hgcm_send_request, esi, eax
|
|
||||||
test eax, eax
|
|
||||||
jnz .hgcm_error
|
|
||||||
|
|
||||||
; Проверяем результат
|
|
||||||
mov eax, [esi + HGCM_HEADER.result]
|
|
||||||
test eax, eax
|
|
||||||
jnz .write_failed
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] Write successful\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.write_failed:
|
|
||||||
DEBUGF 1, "[clip] WRITE_DATA failed: rc=0x%x\n", eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.conversion_failed:
|
|
||||||
DEBUGF 1, "[clip] ERROR: UTF-8 → UTF-16LE conversion failed\n"
|
|
||||||
mov eax, VERR_GENERAL_FAILURE
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error_cleanup:
|
|
||||||
pop eax ; Cleanup stack
|
|
||||||
|
|
||||||
.not_ready:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Clipboard not ready\n"
|
|
||||||
; mov eax, VERR_INVALID_STATE
|
|
||||||
ret
|
|
||||||
|
|
||||||
.invalid_param:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Invalid parameters\n"
|
|
||||||
mov eax, VERR_INVALID_PARAMETER
|
|
||||||
ret
|
|
||||||
|
|
||||||
.hgcm_error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Internal error\n"
|
|
||||||
mov eax, VERR_GENERAL_FAILURE
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: clipboard_report_formats
|
|
||||||
; Без изменений - как в оригинале
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_report_formats uses ebx esi edi, formats:dword
|
|
||||||
DEBUGF 2, "[clip] >>> clipboard_report_formats(0x%x)\n", [formats]
|
|
||||||
|
|
||||||
; Проверка FSM state
|
|
||||||
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
|
|
||||||
jne .not_ready_skip_check
|
|
||||||
|
|
||||||
; При инициализации может быть вызвано до READY - пропускаем проверку
|
|
||||||
.not_ready_skip_check:
|
|
||||||
mov esi, shcl_formats_report
|
|
||||||
test esi, esi
|
|
||||||
jz .error
|
|
||||||
|
|
||||||
lea edi, [esi + SHCL_FORMATS_REPORT.formats]
|
|
||||||
mov eax, [formats]
|
|
||||||
hgcm_set_parm_u32 edi, eax
|
|
||||||
|
|
||||||
; Сохраняем наши форматы
|
|
||||||
mov eax, [formats]
|
|
||||||
mov [clipboard_state.formats_guest], eax
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Reporting formats: 0x%x\n", eax
|
|
||||||
|
|
||||||
mov eax, [clipboard_state.buffer_phys]
|
|
||||||
stdcall hgcm_send_request, esi, eax
|
|
||||||
test eax, eax
|
|
||||||
jnz .hgcm_error
|
|
||||||
|
|
||||||
mov eax, [esi + HGCM_HEADER.result]
|
|
||||||
test eax, eax
|
|
||||||
jnz .report_failed
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Formats reported successfully\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.report_failed:
|
|
||||||
DEBUGF 1, "[clip] REPORT_FORMATS failed: rc=0x%x\n", eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.hgcm_error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: hgcm_send_request failed\n"
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Internal error\n"
|
|
||||||
mov eax, VERR_GENERAL_FAILURE
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: clipboard_handle_event
|
|
||||||
; ИСПРАВЛЕНО: Вызывается ТОЛЬКО когда state == READY
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_handle_event uses ebx esi edi
|
|
||||||
DEBUGF 2, "[clip] >>> clipboard_handle_event\n"
|
|
||||||
|
|
||||||
; Double-check FSM state (должно быть проверено в IRQ, но для надежности)
|
|
||||||
cmp dword [clipboard_state.state], CLIPBOARD_STATE_READY
|
|
||||||
jne .not_ready
|
|
||||||
|
|
||||||
cmp dword [clipboard_state.connected], 1
|
|
||||||
jne .not_connected
|
|
||||||
|
|
||||||
; Получаем сообщение от хоста
|
|
||||||
stdcall clipboard_get_host_msg
|
|
||||||
cmp eax, -1
|
|
||||||
je .error
|
|
||||||
|
|
||||||
; EAX = msg_type, EBX = formats
|
|
||||||
test eax, eax
|
|
||||||
jz .done ; Нет сообщения
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] Host message: type=%d, formats=0x%x\n", eax, ebx
|
|
||||||
|
|
||||||
; Сохраняем форматы хоста
|
|
||||||
mov [clipboard_state.formats_host], ebx
|
|
||||||
|
|
||||||
; Обрабатываем по типу сообщения
|
|
||||||
cmp eax, 0 ; VBOX_SHCL_HOST_MSG_FORMATS_REPORT
|
|
||||||
je .host_formats_changed
|
|
||||||
|
|
||||||
cmp eax, 1 ; VBOX_SHCL_HOST_MSG_READ_DATA
|
|
||||||
je .host_wants_read
|
|
||||||
|
|
||||||
cmp eax, 2 ; VBOX_SHCL_HOST_MSG_READ_DATA_CID
|
|
||||||
je .host_wants_read
|
|
||||||
|
|
||||||
cmp eax, 3 ; VBOX_SHCL_HOST_MSG_REPORT_FORMATS
|
|
||||||
je .host_formats_changed
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] Unknown message type: %d\n", eax
|
|
||||||
jmp .done
|
|
||||||
|
|
||||||
.host_formats_changed:
|
|
||||||
DEBUGF 1, "[clip] Host clipboard formats changed: 0x%x\n", ebx
|
|
||||||
|
|
||||||
; Проверяем есть ли текст
|
|
||||||
test ebx, VBOX_SHCL_FMT_UNICODETEXT
|
|
||||||
jz .no_text_format
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] Host has text, ready to read on demand\n"
|
|
||||||
jmp .done
|
|
||||||
|
|
||||||
.no_text_format:
|
|
||||||
DEBUGF 2, "[clip] Host clipboard has no text\n"
|
|
||||||
jmp .done
|
|
||||||
|
|
||||||
.host_wants_read:
|
|
||||||
DEBUGF 1, "[clip] Host wants to read our clipboard data\n"
|
|
||||||
|
|
||||||
; Проверяем есть ли у нас данные
|
|
||||||
mov eax, [clipboard_state.formats_guest]
|
|
||||||
test eax, eax
|
|
||||||
jz .check_last_data
|
|
||||||
|
|
||||||
; Проверяем текстовый формат
|
|
||||||
test eax, VBOX_SHCL_FMT_UNICODETEXT
|
|
||||||
jz .no_text_to_send
|
|
||||||
|
|
||||||
; Проверяем буфер
|
|
||||||
mov eax, [clipboard_state.last_data_size]
|
|
||||||
test eax, eax
|
|
||||||
jz .send_empty
|
|
||||||
|
|
||||||
; Отправляем данные (уже в UTF-8, будет конвертировано в write_data)
|
|
||||||
DEBUGF 1, "[clip] Sending %d bytes UTF-8 to host\n", eax
|
|
||||||
|
|
||||||
mov edi, [clipboard_state.buffer_virt]
|
|
||||||
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, edi, eax
|
|
||||||
jmp .done
|
|
||||||
|
|
||||||
.check_last_data:
|
|
||||||
mov eax, [clipboard_state.last_data_size]
|
|
||||||
test eax, eax
|
|
||||||
jz .send_empty
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] Sending last read data (%d bytes)\n", eax
|
|
||||||
mov edi, [clipboard_state.buffer_virt]
|
|
||||||
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, edi, eax
|
|
||||||
stdcall clipboard_report_formats, VBOX_SHCL_FMT_UNICODETEXT
|
|
||||||
jmp .done
|
|
||||||
|
|
||||||
.send_empty:
|
|
||||||
DEBUGF 1, "[clip] Sending empty string\n"
|
|
||||||
mov edi, [clipboard_state.buffer_virt]
|
|
||||||
mov byte [edi], 0
|
|
||||||
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, edi, 1
|
|
||||||
jmp .done
|
|
||||||
|
|
||||||
.no_text_to_send:
|
|
||||||
DEBUGF 1, "[clip] Host wants text but we don't have it\n"
|
|
||||||
jmp .done
|
|
||||||
|
|
||||||
.done:
|
|
||||||
DEBUGF 2, "[clip] <<< clipboard_handle_event\n"
|
|
||||||
ret
|
|
||||||
|
|
||||||
.not_ready:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Clipboard not ready (state=%d)\n", \
|
|
||||||
[clipboard_state.state]
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Failed to get host message\n"
|
|
||||||
ret
|
|
||||||
|
|
||||||
.not_connected:
|
|
||||||
DEBUGF 2, "[clip] <<< clipboard_handle_event (not connected)\n"
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; HIGH-LEVEL API с кодировкой
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
proc clipboard_set_text uses ebx esi edi, text_ptr:dword
|
|
||||||
DEBUGF 1, "[clip] >>> clipboard_set_text(0x%x)\n", [text_ptr]
|
|
||||||
|
|
||||||
mov esi, [text_ptr]
|
|
||||||
test esi, esi
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
; Определяем длину UTF-8 строки
|
|
||||||
xor ecx, ecx
|
|
||||||
mov edi, esi
|
|
||||||
.strlen_loop:
|
|
||||||
lodsb
|
|
||||||
test al, al
|
|
||||||
jz .strlen_done
|
|
||||||
inc ecx
|
|
||||||
cmp ecx, VBOX_SHCL_MAX_CHUNK_SIZE - 4
|
|
||||||
jae .strlen_done
|
|
||||||
jmp .strlen_loop
|
|
||||||
|
|
||||||
.strlen_done:
|
|
||||||
test ecx, ecx
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
mov esi, [text_ptr]
|
|
||||||
; inc ecx ; НЕ включаем null - конвертер добавит
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Text length: %d bytes UTF-8\n", ecx
|
|
||||||
|
|
||||||
; clipboard_write_data сконвертирует UTF-8 → UTF-16LE
|
|
||||||
stdcall clipboard_write_data, VBOX_SHCL_FMT_UNICODETEXT, esi, ecx
|
|
||||||
test eax, eax
|
|
||||||
jnz .write_failed
|
|
||||||
|
|
||||||
; Сохраняем в локальный буфер для последующей отправки хосту
|
|
||||||
mov esi, [text_ptr]
|
|
||||||
mov edi, [clipboard_state.buffer_virt]
|
|
||||||
rep movsb
|
|
||||||
|
|
||||||
mov dword [clipboard_state.formats_guest], VBOX_SHCL_FMT_UNICODETEXT
|
|
||||||
mov [clipboard_state.last_data_size], ecx
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.write_failed:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Failed to write text\n"
|
|
||||||
ret
|
|
||||||
|
|
||||||
.invalid_param:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Invalid text\n"
|
|
||||||
mov eax, VERR_INVALID_PARAMETER
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
proc clipboard_get_text uses ebx esi edi, buffer:dword, buffer_size:dword
|
|
||||||
DEBUGF 1, "[clip] >>> clipboard_get_text(buffer=0x%x, size=%d)\n", \
|
|
||||||
[buffer], [buffer_size]
|
|
||||||
|
|
||||||
mov edi, [buffer]
|
|
||||||
test edi, edi
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
mov eax, [buffer_size]
|
|
||||||
test eax, eax
|
|
||||||
jz .invalid_param
|
|
||||||
|
|
||||||
; Проверяем формат хоста
|
|
||||||
mov eax, [clipboard_state.formats_host]
|
|
||||||
test eax, VBOX_SHCL_FMT_UNICODETEXT
|
|
||||||
jz .no_text
|
|
||||||
|
|
||||||
; clipboard_read_data сконвертирует UTF-16LE → UTF-8
|
|
||||||
stdcall clipboard_read_data, VBOX_SHCL_FMT_UNICODETEXT, [buffer], [buffer_size]
|
|
||||||
cmp eax, -1
|
|
||||||
je .error
|
|
||||||
|
|
||||||
DEBUGF 1, "[clip] Got text: %d bytes UTF-8\n", eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.no_text:
|
|
||||||
DEBUGF 2, "[clip] No text in host clipboard\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.invalid_param:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Invalid parameters\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.error:
|
|
||||||
DEBUGF 1, "[clip] ERROR: Failed to read text\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Shutdown
|
|
||||||
; =============================================================================
|
|
||||||
proc clipboard_shutdown uses eax esi
|
|
||||||
DEBUGF 2, "[clip] >>> clipboard_shutdown\n"
|
|
||||||
|
|
||||||
; FSM: → INIT
|
|
||||||
mov dword [clipboard_state.state], CLIPBOARD_STATE_INIT
|
|
||||||
|
|
||||||
cmp dword [clipboard_state.connected], 1
|
|
||||||
jne .not_connected
|
|
||||||
|
|
||||||
stdcall hgcm_disconnect, [clipboard_state.client_id]
|
|
||||||
|
|
||||||
; Free UTF-16 buffer
|
|
||||||
mov eax, [clipboard_state.utf16_buffer]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_utf16_buffer
|
|
||||||
invoke KernelFree, eax
|
|
||||||
|
|
||||||
.no_utf16_buffer:
|
|
||||||
; Free main buffer
|
|
||||||
mov eax, [clipboard_state.buffer_virt]
|
|
||||||
test eax, eax
|
|
||||||
jz .no_buffer
|
|
||||||
invoke KernelFree, eax
|
|
||||||
|
|
||||||
.no_buffer:
|
|
||||||
mov dword [clipboard_state.connected], 0
|
|
||||||
mov dword [clipboard_state.client_id], 0
|
|
||||||
mov dword [clipboard_state.buffer_virt], 0
|
|
||||||
mov dword [clipboard_state.buffer_phys], 0
|
|
||||||
mov dword [clipboard_state.utf16_buffer], 0
|
|
||||||
mov dword [clipboard_state.utf16_size], 0
|
|
||||||
mov dword [clipboard_state.last_data_size], 0
|
|
||||||
|
|
||||||
DEBUGF 2, "[clip] Clipboard shutdown complete\n"
|
|
||||||
|
|
||||||
.not_connected:
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; DATA
|
|
||||||
; =============================================================================
|
|
||||||
align 4
|
|
||||||
shcl_msg_get: rb sizeof.SHCL_MSG_GET
|
|
||||||
shcl_formats_report: rb sizeof.SHCL_FORMATS_REPORT
|
|
||||||
shcl_data_read: rb sizeof.SHCL_DATA_READ
|
|
||||||
shcl_data_write: rb sizeof.SHCL_DATA_WRITE
|
|
||||||
|
|
||||||
220
services/clipboard/clipboard.inc
Normal file
220
services/clipboard/clipboard.inc
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VBox Shared Clipboard Service
|
||||||
|
; Файл : services/clipboard/clipboard.inc
|
||||||
|
;
|
||||||
|
; Архитектура : Async state machine + IOCTL ABI
|
||||||
|
; clipboard_init — HGCM connect + аллокация listener пакета
|
||||||
|
; clipboard_enable — отправка первого MSG_OLD_GET_WAIT (async)
|
||||||
|
; clipboard_on_tick — проверка завершения async запроса + resubmit
|
||||||
|
;
|
||||||
|
; Данные хоста и гостя передаются через IOCTL (приложение):
|
||||||
|
; CLIP_STATUS (10) — статус (formats, host_new)
|
||||||
|
; CLIP_READ (11) — чтение данных хоста (HGCM DATA_READ)
|
||||||
|
; CLIP_WRITE (12) — запись данных гостя (буферизация + HGCM REPORT_FORMATS)
|
||||||
|
;
|
||||||
|
; Источник протокола: VBox 7.2.6
|
||||||
|
; include/VBox/HostServices/VBoxClipboardSvc.h
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
svc_clipboard_name db "CLIPBOARD", 0
|
||||||
|
|
||||||
|
align 4
|
||||||
|
sz_clipboard_hgcm_service db 'VBoxSharedClipboard', 0
|
||||||
|
|
||||||
|
align 4
|
||||||
|
clip_state CLIPBOARD_STATE
|
||||||
|
|
||||||
|
; clipboard_init — Подключиться к HGCM + выделить listener пакет
|
||||||
|
proc clipboard_init uses ebx
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Initializing...\n"
|
||||||
|
|
||||||
|
mov eax, [vbox_device.hgcm_connect_virt]
|
||||||
|
test eax, eax
|
||||||
|
jz .hgcm_not_ready
|
||||||
|
|
||||||
|
cmp dword [clip_state.connected], 1
|
||||||
|
je .already_connected
|
||||||
|
|
||||||
|
; --- Подключиться к HGCM ---
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Connecting to '%s'...\n", sz_clipboard_hgcm_service
|
||||||
|
stdcall hgcm_connect, sz_clipboard_hgcm_service
|
||||||
|
test eax, eax
|
||||||
|
jz .connect_failed
|
||||||
|
|
||||||
|
mov [clip_state.client_id], eax
|
||||||
|
mov dword [clip_state.connected], 1
|
||||||
|
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
|
||||||
|
mov dword [clip_state.error_count], 0
|
||||||
|
mov dword [clip_state.formats_host], 0
|
||||||
|
mov dword [clip_state.formats_guest], 0
|
||||||
|
mov dword [clip_state.has_data], 0
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Connected, client_id=%d\n", eax
|
||||||
|
|
||||||
|
; --- Выделить listener пакет (или переиспользовать существующий) ---
|
||||||
|
; При fire-and-forget disconnect пакет не освобождается (хост может
|
||||||
|
; ещё DMA-писать DONE flag), поэтому переиспользуем при re-enable.
|
||||||
|
cmp dword [clip_state.listen_pkt_virt], 0
|
||||||
|
jne .pkt_ready
|
||||||
|
|
||||||
|
invoke KernelAlloc, 4096
|
||||||
|
test eax, eax
|
||||||
|
jz .alloc_failed
|
||||||
|
|
||||||
|
mov [clip_state.listen_pkt_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [clip_state.listen_pkt_phys], eax
|
||||||
|
.pkt_ready:
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener pkt: virt=0x%x, phys=0x%x\n", \
|
||||||
|
[clip_state.listen_pkt_virt], [clip_state.listen_pkt_phys]
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Initialized OK\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.alloc_failed:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] KernelAlloc failed for listener pkt\n"
|
||||||
|
stdcall hgcm_disconnect, [clip_state.client_id]
|
||||||
|
mov dword [clip_state.connected], 0
|
||||||
|
mov eax, VERR_NO_MEMORY
|
||||||
|
ret
|
||||||
|
|
||||||
|
.connect_failed:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] HGCM connect failed\n"
|
||||||
|
mov eax, VERR_HGCM_SERVICE_NOT_FOUND
|
||||||
|
ret
|
||||||
|
.hgcm_not_ready:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] HGCM not ready\n"
|
||||||
|
mov eax, VERR_NOT_READY
|
||||||
|
ret
|
||||||
|
.already_connected:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Already connected\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_enable — Отправить первый MSG_OLD_GET_WAIT
|
||||||
|
proc clipboard_enable
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enabling (async listener)...\n"
|
||||||
|
|
||||||
|
cmp dword [clip_state.connected], 0
|
||||||
|
jne .connected
|
||||||
|
|
||||||
|
; После disable HGCM отключен — переподключиться
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Not connected, re-initializing...\n"
|
||||||
|
call clipboard_init
|
||||||
|
test eax, eax
|
||||||
|
jnz .init_failed
|
||||||
|
|
||||||
|
.connected:
|
||||||
|
; Отправить первый listener запрос
|
||||||
|
call clipboard_listener_submit
|
||||||
|
test eax, eax
|
||||||
|
jnz .submit_failed
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enabled OK\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.init_failed:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enable: re-init failed (0x%x)\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.submit_failed:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Enable: first submit failed (0x%x)\n", eax
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_on_event — Вызывается из IRQ (dispatcher_dispatch)
|
||||||
|
; ПРИМЕЧАНИЕ: IRQ обработка полностью делегирована on_tick,
|
||||||
|
; поэтому здесь просто возвращаем (событие уже обработано в tick)
|
||||||
|
proc clipboard_on_event
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_on_tick — Вызывается из таймера каждые 100ms
|
||||||
|
proc clipboard_on_tick
|
||||||
|
cmp dword [clip_state.connected], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
cmp dword [clip_state.error_count], CLIP_MAX_ERRORS
|
||||||
|
jae .done
|
||||||
|
|
||||||
|
mov eax, [clip_state.listen_state]
|
||||||
|
|
||||||
|
cmp eax, CLIP_LISTEN_SUBMITTED
|
||||||
|
je .check
|
||||||
|
|
||||||
|
; IDLE — отправить новый запрос
|
||||||
|
call clipboard_listener_submit
|
||||||
|
jmp .done
|
||||||
|
|
||||||
|
.check:
|
||||||
|
call clipboard_listener_check
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_disable — Остановить listener, disconnect HGCM
|
||||||
|
proc clipboard_disable uses edi
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Disabling...\n"
|
||||||
|
|
||||||
|
cmp dword [clip_state.connected], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
; Если listener pending — disconnect fire-and-forget (без busy-wait).
|
||||||
|
; При shutdown VBox не может быстро обработать disconnect из-за pending
|
||||||
|
; MSG_OLD_GET_WAIT, и hgcm_send_request зависает в hgcm_wait_async
|
||||||
|
; на ~30-60 сек (HGCM_TIMEOUT_MS = 500000 итераций busy-loop).
|
||||||
|
cmp dword [clip_state.listen_state], CLIP_LISTEN_SUBMITTED
|
||||||
|
je .fast_disconnect
|
||||||
|
|
||||||
|
; Нет pending запроса — обычный disconnect (sync, быстрый)
|
||||||
|
stdcall hgcm_disconnect, [clip_state.client_id]
|
||||||
|
jmp .cleanup
|
||||||
|
|
||||||
|
.fast_disconnect:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener pending — fire-and-forget disconnect\n"
|
||||||
|
|
||||||
|
mov edi, [vbox_device.hgcm_disconnect_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .cleanup
|
||||||
|
|
||||||
|
hgcm_prepare_header edi, sizeof.HGCM_DISCONNECT, VMMDEV_HGCM_DISCONNECT
|
||||||
|
mov eax, [clip_state.client_id]
|
||||||
|
mov [edi + HGCM_DISCONNECT.client_id], eax
|
||||||
|
|
||||||
|
; Отправить через VMMDev напрямую (non-blocking out dx, eax)
|
||||||
|
; НЕ вызываем hgcm_send_request чтобы избежать hgcm_wait_async
|
||||||
|
stdcall vmmdev_send_request, [vbox_device.hgcm_disconnect_phys]
|
||||||
|
|
||||||
|
.cleanup:
|
||||||
|
; НЕ освобождаем listener пакет — хост может ещё DMA-писать DONE flag
|
||||||
|
; в эту страницу. Пакет будет переиспользован при следующем enable.
|
||||||
|
|
||||||
|
; Освободить буфер guest→host данных
|
||||||
|
cmp dword [clip_guest_buf_ptr], 0
|
||||||
|
je .no_guest_buf
|
||||||
|
invoke KernelFree, [clip_guest_buf_ptr]
|
||||||
|
mov dword [clip_guest_buf_ptr], 0
|
||||||
|
mov dword [clip_guest_buf_size], 0
|
||||||
|
.no_guest_buf:
|
||||||
|
|
||||||
|
mov dword [clip_state.connected], 0
|
||||||
|
mov dword [clip_state.client_id], 0
|
||||||
|
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
|
||||||
|
mov dword [clip_host_new], 0
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Disabled\n"
|
||||||
|
.done:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
include 'clipboard_listener.inc'
|
||||||
|
|
||||||
|
REGISTER_SERVICE svc_clipboard_name, CLIPBOARD_EVENT_MASK, CLIPBOARD_CAPS_MASK, \
|
||||||
|
clipboard_init, clipboard_enable, clipboard_disable, clipboard_on_event, clipboard_on_tick, AUTOSTART_CLIPBOARD
|
||||||
614
services/clipboard/clipboard_listener.inc
Normal file
614
services/clipboard/clipboard_listener.inc
Normal file
@@ -0,0 +1,614 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Clipboard Listener + IOCTL handlers
|
||||||
|
;
|
||||||
|
; Файл: services/clipboard/clipboard_listener.inc
|
||||||
|
;
|
||||||
|
; Listener: async MSG_OLD_GET_WAIT для получения сообщений от хоста
|
||||||
|
; IOCTL: clip_ioctl_status, clip_ioctl_read, clip_ioctl_write
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Размер пакета: HGCM_CALL(44) + 2 * HGCM_PARM(12) = 68
|
||||||
|
CLIP_LISTEN_PKT_SIZE = sizeof.HGCM_CALL + (VBOX_SHCL_CPARMS_MSG_OLD_GET_WAIT * sizeof.HGCM_PARM)
|
||||||
|
|
||||||
|
; Смещения параметров от начала пакета
|
||||||
|
CLIP_LISTEN_PARM0 = sizeof.HGCM_CALL ; offset 44: msg_type
|
||||||
|
CLIP_LISTEN_PARM1 = sizeof.HGCM_CALL + sizeof.HGCM_PARM ; offset 56: formats
|
||||||
|
|
||||||
|
; Начальный размер буфера для HGCM DATA_READ (retry с большим если не хватит)
|
||||||
|
CLIP_INITIAL_BUF_SIZE = 65536 ; 64KB
|
||||||
|
|
||||||
|
; Максимум символов для debug вывода
|
||||||
|
CLIP_DEBUG_MAX_CHARS = 200
|
||||||
|
|
||||||
|
; Статический буфер для cp866-вывода (UTF-16LE → cp866)
|
||||||
|
align 4
|
||||||
|
clip_ascii_buf rb CLIP_DEBUG_MAX_CHARS + 4
|
||||||
|
|
||||||
|
; Host clipboard state (set by async listener, read by IOCTL)
|
||||||
|
align 4
|
||||||
|
clip_host_new dd 0 ; 1 = host clipboard changed since last CLIP_READ
|
||||||
|
|
||||||
|
; Guest → host buffered data (set by CLIP_WRITE, sent on host READ_DATA)
|
||||||
|
align 4
|
||||||
|
clip_guest_buf_ptr dd 0 ; KernelAlloc'd buffer, or 0
|
||||||
|
clip_guest_buf_size dd 0 ; data size in bytes
|
||||||
|
clip_guest_buf_fmt dd 0 ; VBOX_SHCL_FMT_*
|
||||||
|
|
||||||
|
; clipboard_listener_submit — Отправить MSG_OLD_GET_WAIT (async)
|
||||||
|
proc clipboard_listener_submit uses ebx edx edi
|
||||||
|
mov edi, [clip_state.listen_pkt_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
; --- Заполнить HGCM_CALL заголовок ---
|
||||||
|
hgcm_prepare_header edi, CLIP_LISTEN_PKT_SIZE, VMMDEV_HGCM_CALL32
|
||||||
|
|
||||||
|
mov eax, [clip_state.client_id]
|
||||||
|
mov [edi + HGCM_CALL.client_id], eax
|
||||||
|
mov dword [edi + HGCM_CALL.function], VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT
|
||||||
|
mov dword [edi + HGCM_CALL.param_count], VBOX_SHCL_CPARMS_MSG_OLD_GET_WAIT
|
||||||
|
|
||||||
|
; --- parm[0]: msg_type (32BIT, OUT) ---
|
||||||
|
mov dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
||||||
|
mov dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.u.value32], 0
|
||||||
|
mov dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.u.value32 + 4], 0
|
||||||
|
|
||||||
|
; --- parm[1]: formats (32BIT, OUT) ---
|
||||||
|
mov dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
||||||
|
mov dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.u.value32], 0
|
||||||
|
mov dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.u.value32 + 4], 0
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Submitting MSG_OLD_GET_WAIT (async)...\n"
|
||||||
|
|
||||||
|
stdcall vmmdev_send_request, [clip_state.listen_pkt_phys]
|
||||||
|
|
||||||
|
mov eax, [edi + HGCM_HEADER.header.rc]
|
||||||
|
|
||||||
|
cmp eax, VINF_HGCM_ASYNC_EXECUTE
|
||||||
|
je .async_ok
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
js .error
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] MSG_OLD_GET_WAIT completed sync (rc=0x%x)\n", eax
|
||||||
|
mov dword [clip_state.listen_state], CLIP_LISTEN_SUBMITTED
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.async_ok:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] MSG_OLD_GET_WAIT submitted (async)\n"
|
||||||
|
mov dword [clip_state.listen_state], CLIP_LISTEN_SUBMITTED
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] MSG_OLD_GET_WAIT submit failed: rc=0x%x\n", eax
|
||||||
|
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener packet not allocated!\n"
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_listener_check — Проверить завершение async запроса
|
||||||
|
proc clipboard_listener_check uses ebx edx edi
|
||||||
|
mov edi, [clip_state.listen_pkt_virt]
|
||||||
|
|
||||||
|
mfence
|
||||||
|
|
||||||
|
mov eax, [edi + HGCM_HEADER.flags]
|
||||||
|
test eax, VBOX_HGCM_REQ_DONE
|
||||||
|
jz .not_done
|
||||||
|
|
||||||
|
mfence
|
||||||
|
|
||||||
|
mov eax, [edi + HGCM_HEADER.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
js .transport_error
|
||||||
|
|
||||||
|
mov eax, [edi + HGCM_HEADER.result]
|
||||||
|
test eax, eax
|
||||||
|
js .service_error
|
||||||
|
|
||||||
|
mov eax, dword [edi + CLIP_LISTEN_PARM0 + HGCM_PARM.u.value32]
|
||||||
|
mov edx, dword [edi + CLIP_LISTEN_PARM1 + HGCM_PARM.u.value32]
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Got message: type=%d, formats=0x%x\n", eax, edx
|
||||||
|
|
||||||
|
push eax edx
|
||||||
|
stdcall clipboard_listener_dispatch, eax, edx
|
||||||
|
pop edx eax
|
||||||
|
|
||||||
|
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
|
||||||
|
mov dword [clip_state.error_count], 0
|
||||||
|
mov eax, 1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.transport_error:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener transport error: rc=0x%x\n", eax
|
||||||
|
jmp .handle_error
|
||||||
|
|
||||||
|
.service_error:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Listener service error: result=0x%x\n", eax
|
||||||
|
|
||||||
|
.handle_error:
|
||||||
|
mov dword [clip_state.listen_state], CLIP_LISTEN_IDLE
|
||||||
|
inc dword [clip_state.error_count]
|
||||||
|
mov eax, 1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_done:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_listener_dispatch — Обработать сообщение хоста
|
||||||
|
proc clipboard_listener_dispatch stdcall, msg_type:dword, formats:dword
|
||||||
|
|
||||||
|
mov eax, [msg_type]
|
||||||
|
|
||||||
|
cmp eax, VBOX_SHCL_HOST_MSG_FORMATS_REPORT
|
||||||
|
je .formats_report
|
||||||
|
|
||||||
|
cmp eax, VBOX_SHCL_HOST_MSG_READ_DATA
|
||||||
|
je .read_data
|
||||||
|
|
||||||
|
cmp eax, VBOX_SHCL_HOST_MSG_QUIT
|
||||||
|
je .quit
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Unknown msg_type=%d\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.formats_report:
|
||||||
|
; Хост сообщает что у него новые данные в клипборде
|
||||||
|
mov eax, [formats]
|
||||||
|
mov [clip_state.formats_host], eax
|
||||||
|
mov dword [clip_host_new], 1
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Host formats: 0x%x (flagged for app)\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.read_data:
|
||||||
|
; Хост хочет прочитать данные гостя — отправляем буферизованные
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Host requests READ_DATA (formats=0x%x, buf=%d bytes)\n", \
|
||||||
|
[formats], [clip_guest_buf_size]
|
||||||
|
|
||||||
|
cmp dword [clip_guest_buf_ptr], 0
|
||||||
|
je .no_guest_data
|
||||||
|
|
||||||
|
stdcall clipboard_respond_read_data
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_guest_data:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] READ_DATA: no guest data buffered!\n"
|
||||||
|
ret
|
||||||
|
|
||||||
|
.quit:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Host sent QUIT\n"
|
||||||
|
mov dword [clip_state.connected], 0
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_respond_read_data — Отправить буферизованные данные хосту
|
||||||
|
proc clipboard_respond_read_data stdcall uses ebx ecx edx esi edi
|
||||||
|
locals
|
||||||
|
parms rb sizeof.HGCM_PARM * 2
|
||||||
|
endl
|
||||||
|
|
||||||
|
; parm[0]: format (32BIT, IN)
|
||||||
|
lea edi, [parms]
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
||||||
|
mov eax, [clip_guest_buf_fmt]
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32], eax
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
|
||||||
|
|
||||||
|
; parm[1]: data (LINADDR_IN)
|
||||||
|
add edi, sizeof.HGCM_PARM
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_IN
|
||||||
|
mov eax, [clip_guest_buf_size]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
|
||||||
|
mov eax, [clip_guest_buf_ptr]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Sending %d bytes to host (fmt=0x%x)\n", \
|
||||||
|
[clip_guest_buf_size], [clip_guest_buf_fmt]
|
||||||
|
|
||||||
|
lea eax, [parms]
|
||||||
|
stdcall hgcm_call32_pagelist, [clip_state.client_id], \
|
||||||
|
VBOX_SHCL_GUEST_FN_DATA_WRITE, eax, VBOX_SHCL_CPARMS_DATA_WRITE_OLD
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Guest data sent to host OK\n"
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] DATA_WRITE FAILED: 0x%x\n", eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_report_formats — Сообщить хосту о форматах гостевого clipboard
|
||||||
|
proc clipboard_report_formats stdcall uses ebx ecx edx esi edi, formats:dword
|
||||||
|
locals
|
||||||
|
parms rb sizeof.HGCM_PARM
|
||||||
|
endl
|
||||||
|
lea edi, [parms]
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
||||||
|
mov eax, [formats]
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32], eax
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
|
||||||
|
|
||||||
|
lea eax, [parms]
|
||||||
|
stdcall hgcm_call32_pagelist, [clip_state.client_id], \
|
||||||
|
VBOX_SHCL_GUEST_FN_REPORT_FORMATS, eax, VBOX_SHCL_CPARMS_REPORT_FORMATS
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Reported formats 0x%x to host\n", [formats]
|
||||||
|
ret
|
||||||
|
.error:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Report formats FAILED: 0x%x\n", eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clip_ioctl_status — IOCTL 10: Статус clipboard
|
||||||
|
proc clip_ioctl_status stdcall uses ebx, ioctl_ptr:dword
|
||||||
|
mov ebx, [ioctl_ptr]
|
||||||
|
cmp [ebx + IOCTL.out_size], 12
|
||||||
|
jb .fail
|
||||||
|
|
||||||
|
mov eax, [ebx + IOCTL.output]
|
||||||
|
|
||||||
|
mov ecx, [clip_state.connected]
|
||||||
|
mov [eax], ecx
|
||||||
|
|
||||||
|
mov ecx, [clip_state.formats_host]
|
||||||
|
mov [eax + 4], ecx
|
||||||
|
|
||||||
|
mov ecx, [clip_host_new]
|
||||||
|
mov [eax + 8], ecx
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail:
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clip_ioctl_read — IOCTL 11: Прочитать clipboard хоста
|
||||||
|
proc clip_ioctl_read stdcall uses ebx ecx edx esi edi, ioctl_ptr:dword
|
||||||
|
locals
|
||||||
|
parms rb sizeof.HGCM_PARM * 3
|
||||||
|
kern_buf dd ?
|
||||||
|
kern_size dd ?
|
||||||
|
req_format dd ?
|
||||||
|
endl
|
||||||
|
mov ebx, [ioctl_ptr]
|
||||||
|
|
||||||
|
; Validate input/output
|
||||||
|
cmp [ebx + IOCTL.inp_size], 4
|
||||||
|
jb .fail
|
||||||
|
cmp [ebx + IOCTL.out_size], 4
|
||||||
|
jb .fail
|
||||||
|
cmp dword [clip_state.connected], 0
|
||||||
|
je .fail
|
||||||
|
|
||||||
|
; Сбросить host_new СРАЗУ — чтобы при ошибке HGCM приложение
|
||||||
|
; не повторяло CLIP_READ бесконечно (retry storm).
|
||||||
|
; Новое изменение хоста снова выставит host_new=1 через listener.
|
||||||
|
mov dword [clip_host_new], 0
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: out_size=%d\n", [ebx + IOCTL.out_size]
|
||||||
|
|
||||||
|
; Get requested format
|
||||||
|
mov eax, [ebx + IOCTL.input]
|
||||||
|
mov eax, [eax]
|
||||||
|
mov [req_format], eax
|
||||||
|
|
||||||
|
; Calculate kernel buffer size: max(out_size - 4, CLIP_INITIAL_BUF_SIZE)
|
||||||
|
mov ecx, [ebx + IOCTL.out_size]
|
||||||
|
sub ecx, 4
|
||||||
|
cmp ecx, CLIP_INITIAL_BUF_SIZE
|
||||||
|
jae .size_ok
|
||||||
|
mov ecx, CLIP_INITIAL_BUF_SIZE
|
||||||
|
.size_ok:
|
||||||
|
; Round up to page
|
||||||
|
add ecx, (PAGE_SIZE - 1)
|
||||||
|
and ecx, PAGE_BASE_MASK
|
||||||
|
mov [kern_size], ecx
|
||||||
|
|
||||||
|
; Allocate kernel buffer
|
||||||
|
invoke KernelAlloc, ecx
|
||||||
|
test eax, eax
|
||||||
|
jz .no_mem
|
||||||
|
mov [kern_buf], eax
|
||||||
|
|
||||||
|
.do_read:
|
||||||
|
; --- Заполнить HGCM DATA_READ параметры ---
|
||||||
|
lea edi, [parms]
|
||||||
|
|
||||||
|
; parm[0]: format (32BIT, IN)
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
||||||
|
mov eax, [req_format]
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32], eax
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
|
||||||
|
|
||||||
|
; parm[1]: buffer (LINADDR_OUT)
|
||||||
|
add edi, sizeof.HGCM_PARM
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_LINADDR_OUT
|
||||||
|
mov eax, [kern_size]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.size], eax
|
||||||
|
mov eax, [kern_buf]
|
||||||
|
mov dword [edi + HGCM_PARM.u.LinAddr.offset], eax
|
||||||
|
|
||||||
|
; parm[2]: size (32BIT, OUT)
|
||||||
|
add edi, sizeof.HGCM_PARM
|
||||||
|
mov dword [edi + HGCM_PARM.type], HGCM_PARM_TYPE_32BIT
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32], 0
|
||||||
|
mov dword [edi + HGCM_PARM.u.value32 + 4], 0
|
||||||
|
|
||||||
|
; --- Вызвать HGCM DATA_READ ---
|
||||||
|
lea eax, [parms]
|
||||||
|
stdcall hgcm_call32_pagelist, [clip_state.client_id], \
|
||||||
|
VBOX_SHCL_GUEST_FN_DATA_READ, eax, VBOX_SHCL_CPARMS_DATA_READ
|
||||||
|
|
||||||
|
; Прочитать actual_size из parm[2]
|
||||||
|
lea edi, [parms]
|
||||||
|
mov ecx, dword [edi + 2 * sizeof.HGCM_PARM + HGCM_PARM.u.value32]
|
||||||
|
|
||||||
|
; Проверить результат HGCM
|
||||||
|
test eax, eax
|
||||||
|
jnz .check_overflow
|
||||||
|
|
||||||
|
; Успех — данные прочитаны
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: OK, %d bytes (fmt=0x%x)\n", ecx, [req_format]
|
||||||
|
|
||||||
|
; Записать actual_size в output[0]
|
||||||
|
mov ebx, [ioctl_ptr]
|
||||||
|
mov edi, [ebx + IOCTL.output]
|
||||||
|
mov [edi], ecx
|
||||||
|
|
||||||
|
; Хватает ли места в output для данных?
|
||||||
|
mov eax, [ebx + IOCTL.out_size]
|
||||||
|
sub eax, 4
|
||||||
|
cmp eax, ecx
|
||||||
|
jb .app_overflow
|
||||||
|
|
||||||
|
; Скопировать данные из kern_buf в output + 4
|
||||||
|
push ecx
|
||||||
|
mov esi, [kern_buf]
|
||||||
|
add edi, 4
|
||||||
|
cld
|
||||||
|
rep movsb
|
||||||
|
pop ecx
|
||||||
|
|
||||||
|
; Debug: вывести текст если это UNICODETEXT
|
||||||
|
cmp dword [req_format], VBOX_SHCL_FMT_UNICODETEXT
|
||||||
|
jne .read_ok
|
||||||
|
cmp ecx, 4
|
||||||
|
jb .read_ok
|
||||||
|
stdcall clipboard_utf16_to_debug, [kern_buf], ecx
|
||||||
|
|
||||||
|
.read_ok:
|
||||||
|
invoke KernelFree, [kern_buf]
|
||||||
|
xor eax, eax ; success
|
||||||
|
ret
|
||||||
|
|
||||||
|
.app_overflow:
|
||||||
|
; Буфер приложения мал — actual_size записан, данных нет
|
||||||
|
invoke KernelFree, [kern_buf]
|
||||||
|
mov eax, 1 ; buffer overflow
|
||||||
|
ret
|
||||||
|
|
||||||
|
.check_overflow:
|
||||||
|
; HGCM вернул ошибку — если actual_size > kern_size, это overflow
|
||||||
|
; Попробовать с большим буфером
|
||||||
|
cmp ecx, [kern_size]
|
||||||
|
jbe .hgcm_error ; не overflow, реальная ошибка
|
||||||
|
|
||||||
|
; actual_size > kern_size — нужен буфер побольше
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: overflow, need %d bytes\n", ecx
|
||||||
|
|
||||||
|
invoke KernelFree, [kern_buf]
|
||||||
|
|
||||||
|
; Выделить буфер нужного размера (round up to page)
|
||||||
|
add ecx, (PAGE_SIZE - 1)
|
||||||
|
and ecx, PAGE_BASE_MASK
|
||||||
|
mov [kern_size], ecx
|
||||||
|
invoke KernelAlloc, ecx
|
||||||
|
test eax, eax
|
||||||
|
jz .no_mem
|
||||||
|
mov [kern_buf], eax
|
||||||
|
|
||||||
|
jmp .do_read ; retry
|
||||||
|
|
||||||
|
.hgcm_error:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: HGCM error 0x%x\n", eax
|
||||||
|
; Записать actual_size если возможно
|
||||||
|
mov ebx, [ioctl_ptr]
|
||||||
|
cmp [ebx + IOCTL.out_size], 4
|
||||||
|
jb .hgcm_err_ret
|
||||||
|
mov edi, [ebx + IOCTL.output]
|
||||||
|
mov [edi], ecx
|
||||||
|
.hgcm_err_ret:
|
||||||
|
invoke KernelFree, [kern_buf]
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_mem:
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_READ: KernelAlloc failed\n"
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail:
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clip_ioctl_write — IOCTL 12: Записать данные в clipboard хоста
|
||||||
|
proc clip_ioctl_write stdcall uses ebx ecx edx esi edi, ioctl_ptr:dword
|
||||||
|
mov ebx, [ioctl_ptr]
|
||||||
|
|
||||||
|
cmp [ebx + IOCTL.inp_size], 8 ; минимум: dd format + хотя бы 4 байта данных
|
||||||
|
jb .fail
|
||||||
|
cmp dword [clip_state.connected], 0
|
||||||
|
je .fail
|
||||||
|
|
||||||
|
; Прочитать format и data size
|
||||||
|
mov esi, [ebx + IOCTL.input]
|
||||||
|
mov ecx, [esi] ; format
|
||||||
|
mov edx, [ebx + IOCTL.inp_size]
|
||||||
|
sub edx, 4 ; data_size = inp_size - 4
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_WRITE: %d bytes, fmt=0x%x\n", edx, ecx
|
||||||
|
|
||||||
|
; Освободить предыдущий буфер
|
||||||
|
cmp dword [clip_guest_buf_ptr], 0
|
||||||
|
je .no_old
|
||||||
|
push ecx edx esi
|
||||||
|
invoke KernelFree, [clip_guest_buf_ptr]
|
||||||
|
mov dword [clip_guest_buf_ptr], 0
|
||||||
|
pop esi edx ecx
|
||||||
|
.no_old:
|
||||||
|
|
||||||
|
; Выделить новый буфер (round up to page, min 4096)
|
||||||
|
push ecx edx esi
|
||||||
|
mov eax, edx
|
||||||
|
add eax, (PAGE_SIZE - 1)
|
||||||
|
and eax, PAGE_BASE_MASK
|
||||||
|
cmp eax, PAGE_SIZE
|
||||||
|
jae .alloc_go
|
||||||
|
mov eax, PAGE_SIZE
|
||||||
|
.alloc_go:
|
||||||
|
invoke KernelAlloc, eax
|
||||||
|
test eax, eax
|
||||||
|
jz .no_mem
|
||||||
|
mov [clip_guest_buf_ptr], eax
|
||||||
|
pop esi edx ecx
|
||||||
|
|
||||||
|
mov [clip_guest_buf_size], edx
|
||||||
|
mov [clip_guest_buf_fmt], ecx
|
||||||
|
|
||||||
|
; Скопировать данные из input + 4 в ядерный буфер
|
||||||
|
add esi, 4 ; skip format field
|
||||||
|
mov edi, [clip_guest_buf_ptr]
|
||||||
|
mov ecx, edx
|
||||||
|
cld
|
||||||
|
rep movsb
|
||||||
|
|
||||||
|
; Сообщить хосту что у гостя есть данные
|
||||||
|
stdcall clipboard_report_formats, [clip_guest_buf_fmt]
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_mem:
|
||||||
|
pop esi edx ecx
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] CLIP_WRITE: KernelAlloc failed\n"
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail:
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; clipboard_utf16_to_debug — Конвертировать UTF-16LE -> cp866 и вывести
|
||||||
|
proc clipboard_utf16_to_debug stdcall uses ebx ecx edx esi edi, buf_ptr:dword, buf_size:dword
|
||||||
|
|
||||||
|
mov esi, [buf_ptr]
|
||||||
|
mov edi, clip_ascii_buf
|
||||||
|
mov ecx, [buf_size]
|
||||||
|
shr ecx, 1 ; количество UTF-16 символов (size / 2)
|
||||||
|
|
||||||
|
; Ограничить до CLIP_DEBUG_MAX_CHARS - 1
|
||||||
|
cmp ecx, CLIP_DEBUG_MAX_CHARS - 1
|
||||||
|
jbe .len_ok
|
||||||
|
mov ecx, CLIP_DEBUG_MAX_CHARS - 1
|
||||||
|
.len_ok:
|
||||||
|
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done_empty
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
lodsw ; ax = UTF-16LE char (si += 2)
|
||||||
|
|
||||||
|
; Null terminator?
|
||||||
|
test ax, ax
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
; --- ASCII range (< 0x80) ---
|
||||||
|
cmp ax, 0x80
|
||||||
|
jb .ascii
|
||||||
|
|
||||||
|
; --- Cyrillic: U+0410..U+044F → cp866 ---
|
||||||
|
cmp ax, 0x0410
|
||||||
|
jb .check_yo
|
||||||
|
cmp ax, 0x0440
|
||||||
|
jb .cyr_80 ; U+0410..U+043F → cp866 0x80..0xAF
|
||||||
|
cmp ax, 0x0450
|
||||||
|
jb .cyr_e0 ; U+0440..U+044F → cp866 0xE0..0xEF
|
||||||
|
jmp .check_yo
|
||||||
|
|
||||||
|
.cyr_80:
|
||||||
|
sub ax, 0x0410
|
||||||
|
add al, 0x80
|
||||||
|
jmp .store
|
||||||
|
|
||||||
|
.cyr_e0:
|
||||||
|
sub ax, 0x0440
|
||||||
|
add al, 0xE0
|
||||||
|
jmp .store
|
||||||
|
|
||||||
|
.check_yo:
|
||||||
|
cmp ax, 0x0401 ; Ё → cp866 0xF0
|
||||||
|
je .yo_upper
|
||||||
|
cmp ax, 0x0451 ; ё → cp866 0xF1
|
||||||
|
je .yo_lower
|
||||||
|
|
||||||
|
; Unknown non-ASCII → '?'
|
||||||
|
mov al, '?'
|
||||||
|
jmp .store
|
||||||
|
|
||||||
|
.yo_upper:
|
||||||
|
mov al, 0xF0
|
||||||
|
jmp .store
|
||||||
|
|
||||||
|
.yo_lower:
|
||||||
|
mov al, 0xF1
|
||||||
|
jmp .store
|
||||||
|
|
||||||
|
.ascii:
|
||||||
|
; Контрольные символы (< 0x20) кроме \n, \r, \t → пропускаем
|
||||||
|
cmp al, 0x20
|
||||||
|
jae .store
|
||||||
|
cmp al, 0x0A
|
||||||
|
je .store
|
||||||
|
cmp al, 0x0D
|
||||||
|
je .store
|
||||||
|
cmp al, 0x09
|
||||||
|
je .store
|
||||||
|
jmp .skip
|
||||||
|
|
||||||
|
.store:
|
||||||
|
stosb
|
||||||
|
.skip:
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
xor al, al
|
||||||
|
stosb
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Text: '%s'\n", clip_ascii_buf
|
||||||
|
ret
|
||||||
|
|
||||||
|
.done_empty:
|
||||||
|
mov byte [clip_ascii_buf], 0
|
||||||
|
DEBUGF __DEBUG_CB__, "[VBoxGuest] [Clipboard] Text: (empty)\n"
|
||||||
|
ret
|
||||||
|
endp
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; Display Service - Auto-resize on window change
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
align 4
|
|
||||||
kos_display_ptr dd ?
|
|
||||||
|
|
||||||
proc display_change uses ebx ecx edx esi edi
|
|
||||||
DEBUGF 2, "display_change\n"
|
|
||||||
|
|
||||||
; Request display change information
|
|
||||||
mov eax, [vbox_device.display_phys]
|
|
||||||
stdcall vmmdev_request_perform, eax
|
|
||||||
|
|
||||||
mov ebx, [vbox_device.display_virt]
|
|
||||||
mov edi, [ebx + VMMDEV_DISPLAY_CHANGE.x_res]
|
|
||||||
mov esi, [ebx + VMMDEV_DISPLAY_CHANGE.y_res]
|
|
||||||
mov ecx, [ebx + VMMDEV_DISPLAY_CHANGE.bpp]
|
|
||||||
|
|
||||||
DEBUGF 2, "[display] Requested resolution: %dx%d %dbpp\n", edi, esi, ecx
|
|
||||||
|
|
||||||
; Validate minimum
|
|
||||||
cmp edi, DISP_W_MIN
|
|
||||||
jb .skip
|
|
||||||
cmp esi, DISP_H_MIN
|
|
||||||
jb .skip
|
|
||||||
|
|
||||||
; Validate maximum
|
|
||||||
cmp edi, DISP_W_MAX
|
|
||||||
ja .skip
|
|
||||||
cmp esi, DISP_H_MAX
|
|
||||||
ja .skip
|
|
||||||
|
|
||||||
; Validate BPP
|
|
||||||
cmp ecx, 32
|
|
||||||
je .valid
|
|
||||||
cmp ecx, 24
|
|
||||||
je .valid
|
|
||||||
cmp ecx, 16
|
|
||||||
je .valid
|
|
||||||
cmp ecx, 8
|
|
||||||
jne .skip
|
|
||||||
|
|
||||||
.valid:
|
|
||||||
DEBUGF 2, "[display] Setting resolution %dx%d %dbpp\n", edi, esi, ecx
|
|
||||||
|
|
||||||
; Set video mode using VGA
|
|
||||||
cli
|
|
||||||
vga_set_mode edi, esi, ecx
|
|
||||||
sti
|
|
||||||
|
|
||||||
; Update KolibriOS display structure
|
|
||||||
mov eax, [kos_display_ptr]
|
|
||||||
mov [eax + DISPLAY.width], edi
|
|
||||||
mov [eax + DISPLAY.height], esi
|
|
||||||
mov [eax + DISPLAY.bits_per_pixel], ecx
|
|
||||||
|
|
||||||
; Calculate pitch
|
|
||||||
push ebx
|
|
||||||
mov ebx, ecx
|
|
||||||
shr ecx, 3
|
|
||||||
imul ecx, edi
|
|
||||||
mov [eax + DISPLAY.pitch], ecx
|
|
||||||
pop ebx
|
|
||||||
|
|
||||||
; Apply changes
|
|
||||||
mov eax, edi
|
|
||||||
mov edx, esi
|
|
||||||
dec eax
|
|
||||||
dec edx
|
|
||||||
invoke SetScreen
|
|
||||||
|
|
||||||
DEBUGF 2, "[display] Resolution set successfully\n"
|
|
||||||
|
|
||||||
.skip:
|
|
||||||
DEBUGF 2, "[trace] <<< display_change\n"
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
206
services/display/display.inc
Normal file
206
services/display/display.inc
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Display Service (Auto-resize)
|
||||||
|
; Назначение : Автоматическое изменение разрешения экрана при resize окна VBox
|
||||||
|
; Файл : services/display/display.inc
|
||||||
|
;
|
||||||
|
; Логика задержки (паттерн heartbeat):
|
||||||
|
; Event → запомнить время, поставить флаг pending
|
||||||
|
; Tick → если pending и прошло 2 секунды → display_change
|
||||||
|
; Каждое новое событие сбрасывает таймер (debounce)
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
svc_display_name db "DISPLAY", 0
|
||||||
|
|
||||||
|
; --- Display Data ---
|
||||||
|
align 4
|
||||||
|
vbox_display_enabled dd 0
|
||||||
|
kos_display_ptr dd 0
|
||||||
|
|
||||||
|
; Отложенное обновление (debounce)
|
||||||
|
vbox_display_pending dd 0 ; 1 = есть отложенный запрос
|
||||||
|
vbox_display_event_time dd 0 ; timer_ticks момента последнего события
|
||||||
|
DISPLAY_DELAY_TICKS equ 50 ; 500ms (50 * 10ms)
|
||||||
|
|
||||||
|
; Статический буфер для запроса GetDisplayChangeRequest2
|
||||||
|
align 4
|
||||||
|
vmmdev_display_change_s VMMDEV_GET_DISPLAY_CHANGE_REQUEST2 \
|
||||||
|
<sizeof.VMMDEV_GET_DISPLAY_CHANGE_REQUEST2, \
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION, \
|
||||||
|
VMMDEV_REQ_GET_DISPLAY_CHANGE_2, \
|
||||||
|
0, 0, VMMDEV_REQUESTOR_KERNEL>, \
|
||||||
|
0, 0, 0, 1, 0 ; x_res, y_res, bpp, event_ack=1, display=0
|
||||||
|
|
||||||
|
; --- BGA Macros ---
|
||||||
|
|
||||||
|
macro bga_write index_val, data_val {
|
||||||
|
mov eax, index_val
|
||||||
|
mov dx, VBE_DISPI_IOPORT_INDEX
|
||||||
|
out dx, ax
|
||||||
|
mov eax, data_val
|
||||||
|
mov dx, VBE_DISPI_IOPORT_DATA
|
||||||
|
out dx, ax
|
||||||
|
}
|
||||||
|
|
||||||
|
macro bga_set_mode w, h, bpp {
|
||||||
|
bga_write VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED
|
||||||
|
bga_write VBE_DISPI_INDEX_XRES, w
|
||||||
|
bga_write VBE_DISPI_INDEX_YRES, h
|
||||||
|
bga_write VBE_DISPI_INDEX_BPP, bpp
|
||||||
|
bga_write VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED or VBE_DISPI_LFB_ENABLED
|
||||||
|
}
|
||||||
|
|
||||||
|
; vmmdev_display_init — Инициализация подсистемы дисплея
|
||||||
|
proc vmmdev_display_init
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Initializing...\n"
|
||||||
|
|
||||||
|
; Получить указатель на DISPLAY структуру ядра KolibriOS
|
||||||
|
invoke GetDisplay
|
||||||
|
mov [kos_display_ptr], eax
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] KOS display struct: 0x%x\n", eax
|
||||||
|
|
||||||
|
; Получить физический адрес буфера запроса
|
||||||
|
mov eax, vmmdev_display_change_s
|
||||||
|
mov [vbox_device.display_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.display_phys], eax
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Buffer: virt=0x%x, phys=0x%x\n", \
|
||||||
|
vmmdev_display_change_s, eax
|
||||||
|
|
||||||
|
mov dword [vbox_display_enabled], 1
|
||||||
|
|
||||||
|
; Сразу обработать текущее разрешение (без задержки)
|
||||||
|
call display_change
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Initialized OK\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; display_handle_event — Обработка события (из IRQ)
|
||||||
|
proc display_handle_event
|
||||||
|
test eax, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
|
||||||
|
jz .done
|
||||||
|
cmp dword [vbox_display_enabled], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
; Запомнить время события (сбросить таймер при повторных)
|
||||||
|
invoke GetTimerTicks
|
||||||
|
mov [vbox_display_event_time], eax
|
||||||
|
|
||||||
|
; Поставить флаг
|
||||||
|
mov dword [vbox_display_pending], 1
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Event queued, will apply in 2s\n"
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; display_tick — Вызывается периодически из timer tick
|
||||||
|
proc display_tick
|
||||||
|
cmp dword [vbox_display_pending], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
invoke GetTimerTicks
|
||||||
|
sub eax, [vbox_display_event_time]
|
||||||
|
cmp eax, DISPLAY_DELAY_TICKS
|
||||||
|
jb .done
|
||||||
|
|
||||||
|
; Сбросить флаг ДО обработки (новое событие во время display_change
|
||||||
|
; не потеряется — оно поставит pending заново)
|
||||||
|
mov dword [vbox_display_pending], 0
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Delay elapsed, applying...\n"
|
||||||
|
call display_change
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; display_change — Запросить и применить новое разрешение
|
||||||
|
proc display_change uses ebx ecx edx esi edi
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] display_change\n"
|
||||||
|
|
||||||
|
mov ebx, [vbox_device.display_virt]
|
||||||
|
test ebx, ebx
|
||||||
|
jz .skip
|
||||||
|
|
||||||
|
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.header.rc], 0
|
||||||
|
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.event_ack], 1
|
||||||
|
mov dword [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.display], 0
|
||||||
|
|
||||||
|
mov eax, [vbox_device.display_phys]
|
||||||
|
stdcall vmmdev_send_request, eax
|
||||||
|
|
||||||
|
mov ebx, [vbox_device.display_virt]
|
||||||
|
mov edi, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.x_res]
|
||||||
|
mov esi, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.y_res]
|
||||||
|
mov ecx, [ebx + VMMDEV_GET_DISPLAY_CHANGE_REQUEST2.bpp]
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Requested: %dx%d %dbpp\n", edi, esi, ecx
|
||||||
|
|
||||||
|
; Валидация
|
||||||
|
cmp edi, DISP_W_MIN
|
||||||
|
jb .skip
|
||||||
|
cmp esi, DISP_H_MIN
|
||||||
|
jb .skip
|
||||||
|
cmp edi, DISP_W_MAX
|
||||||
|
ja .skip
|
||||||
|
cmp esi, DISP_H_MAX
|
||||||
|
ja .skip
|
||||||
|
|
||||||
|
cmp ecx, 32
|
||||||
|
je .valid
|
||||||
|
cmp ecx, 24
|
||||||
|
je .valid
|
||||||
|
cmp ecx, 16
|
||||||
|
je .valid
|
||||||
|
cmp ecx, 8
|
||||||
|
jne .skip
|
||||||
|
|
||||||
|
.valid:
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Setting %dx%d %dbpp\n", edi, esi, ecx
|
||||||
|
|
||||||
|
; BGA под cli (как в v5)
|
||||||
|
cli
|
||||||
|
bga_set_mode edi, esi, ecx
|
||||||
|
sti
|
||||||
|
|
||||||
|
; Обновить DISPLAY структуру ядра
|
||||||
|
mov eax, [kos_display_ptr]
|
||||||
|
mov [eax + DISPLAY.width], edi
|
||||||
|
mov [eax + DISPLAY.height], esi
|
||||||
|
mov [eax + DISPLAY.bits_per_pixel], ecx
|
||||||
|
|
||||||
|
; pitch = (bpp / 8) * width
|
||||||
|
push ebx
|
||||||
|
mov ebx, ecx
|
||||||
|
shr ecx, 3
|
||||||
|
imul ecx, edi
|
||||||
|
mov [eax + DISPLAY.pitch], ecx
|
||||||
|
pop ebx
|
||||||
|
|
||||||
|
; SetScreen(width-1, height-1)
|
||||||
|
mov eax, edi
|
||||||
|
mov edx, esi
|
||||||
|
dec eax
|
||||||
|
dec edx
|
||||||
|
invoke SetScreen
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Resolution set successfully\n"
|
||||||
|
|
||||||
|
.skip:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; display_disable — Отключить подсистему дисплея
|
||||||
|
proc display_disable
|
||||||
|
DEBUGF __DEBUG_DISPLAY__, "[VBoxGuest] [Display] Disabling...\n"
|
||||||
|
mov dword [vbox_display_enabled], 0
|
||||||
|
mov dword [vbox_display_pending], 0
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
REGISTER_SERVICE svc_display_name, DISPLAY_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_GRAPHICS, \
|
||||||
|
vmmdev_display_init, 0, display_disable, display_handle_event, display_tick, AUTOSTART_DISPLAY
|
||||||
263
services/guest_props/guest_props.inc
Normal file
263
services/guest_props/guest_props.inc
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VBox Guest Properties Service
|
||||||
|
; Назначение : Обмен ключ-значение свойствами между хостом и гостем
|
||||||
|
; Файл : services/guest_props/guest_props.inc
|
||||||
|
; Протокол : HGCM (VBoxGuestPropSvc)
|
||||||
|
;
|
||||||
|
; Функционал:
|
||||||
|
; - Подключение к HGCM сервису VBoxGuestPropSvc
|
||||||
|
; - Установка начальных свойств гостевой ОС
|
||||||
|
; - GET_PROP / SET_PROP_VALUE / DEL_PROP / ENUM_PROPS
|
||||||
|
;
|
||||||
|
; Стандартные свойства (устанавливаются при инициализации):
|
||||||
|
; /VirtualBox/GuestInfo/OS/Product = "KolibriOS"
|
||||||
|
; /VirtualBox/GuestInfo/OS/Release = "1.0"
|
||||||
|
; /VirtualBox/GuestAdd/VersionExt = "1.0.0"
|
||||||
|
; /VirtualBox/GuestAdd/Revision = "1"
|
||||||
|
;
|
||||||
|
; Аналог в Linux: VBoxService --property
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
svc_guestprop_name db "GUEST_PROPS", 0
|
||||||
|
|
||||||
|
; HGCM service name on host
|
||||||
|
align 4
|
||||||
|
sz_guestprop_hgcm_service db 'VBoxGuestPropSvc', 0
|
||||||
|
|
||||||
|
; --- Guest Properties Data ---
|
||||||
|
align 4
|
||||||
|
gp_state GUEST_PROP_STATE
|
||||||
|
|
||||||
|
; Буферы выделяются динамически в guestprop_init, указатели в gp_state
|
||||||
|
|
||||||
|
; --- Строки стандартных свойств ---
|
||||||
|
align 4
|
||||||
|
gp_key_os_product db '/VirtualBox/GuestInfo/OS/Product', 0
|
||||||
|
gp_val_os_product db 'KolibriOS', 0
|
||||||
|
|
||||||
|
gp_key_os_release db '/VirtualBox/GuestInfo/OS/Release', 0
|
||||||
|
gp_val_os_release db '1.0', 0
|
||||||
|
|
||||||
|
gp_key_version db '/VirtualBox/GuestAdd/VersionExt', 0
|
||||||
|
gp_val_version db '1.0.0', 0
|
||||||
|
|
||||||
|
gp_key_revision db '/VirtualBox/GuestAdd/Revision', 0
|
||||||
|
gp_val_revision db '1', 0
|
||||||
|
|
||||||
|
; guestprop_init — Инициализация сервиса Guest Properties
|
||||||
|
proc guestprop_init uses ebx ecx edx esi edi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Initializing...\n"
|
||||||
|
|
||||||
|
; Проверка что HGCM готов
|
||||||
|
mov eax, [vbox_device.hgcm_connect_virt]
|
||||||
|
test eax, eax
|
||||||
|
jz .hgcm_not_ready
|
||||||
|
|
||||||
|
; Уже подключены?
|
||||||
|
cmp dword [gp_state.connected], 1
|
||||||
|
je .already_connected
|
||||||
|
|
||||||
|
; --- Выделить буферы ---
|
||||||
|
invoke KernelAlloc, 4096 ; gp_enum_buf (page-aligned для HGCM)
|
||||||
|
test eax, eax
|
||||||
|
jz .alloc_fail
|
||||||
|
mov [gp_state.enum_buf_ptr], eax
|
||||||
|
|
||||||
|
invoke KernelAlloc, 4096 ; small_bufs: parms+name+value+flags
|
||||||
|
test eax, eax
|
||||||
|
jz .alloc_fail_free1
|
||||||
|
mov [gp_state.small_bufs_ptr], eax
|
||||||
|
|
||||||
|
; Вычислить указатели внутри small_bufs
|
||||||
|
lea ecx, [eax + 0]
|
||||||
|
mov [gp_state.parms_ptr], ecx ; +0: 48 байт (sizeof.HGCM_PARM * 4)
|
||||||
|
lea ecx, [eax + 48]
|
||||||
|
mov [gp_state.name_buf_ptr], ecx ; +48: 256 байт
|
||||||
|
lea ecx, [eax + 304]
|
||||||
|
mov [gp_state.value_buf_ptr], ecx ; +304: 1024 байт
|
||||||
|
lea ecx, [eax + 1328]
|
||||||
|
mov [gp_state.flags_buf_ptr], ecx ; +1328: 128 байт
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Buffers allocated\n"
|
||||||
|
|
||||||
|
; Подключиться к HGCM сервису
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Connecting to '%s'...\n", sz_guestprop_hgcm_service
|
||||||
|
stdcall hgcm_connect, sz_guestprop_hgcm_service
|
||||||
|
test eax, eax
|
||||||
|
jz .connect_failed
|
||||||
|
|
||||||
|
mov [gp_state.client_id], eax
|
||||||
|
mov dword [gp_state.connected], 1
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Connected, client_id=%d\n", eax
|
||||||
|
|
||||||
|
; Установить начальные свойства
|
||||||
|
call guestprop_set_initial_props
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Initialized OK\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.connect_failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] HGCM connect failed\n"
|
||||||
|
call guestprop_free_bufs
|
||||||
|
mov eax, VERR_HGCM_SERVICE_NOT_FOUND
|
||||||
|
ret
|
||||||
|
|
||||||
|
.alloc_fail_free1:
|
||||||
|
invoke KernelFree, [gp_state.enum_buf_ptr]
|
||||||
|
mov dword [gp_state.enum_buf_ptr], 0
|
||||||
|
.alloc_fail:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] KernelAlloc failed\n"
|
||||||
|
mov eax, VERR_NO_MEMORY
|
||||||
|
ret
|
||||||
|
|
||||||
|
.hgcm_not_ready:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] HGCM not ready\n"
|
||||||
|
mov eax, VERR_NOT_READY
|
||||||
|
ret
|
||||||
|
|
||||||
|
.already_connected:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Already connected\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; guestprop_set_initial_props — Установить стандартные свойства ОС
|
||||||
|
proc guestprop_set_initial_props uses eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Setting initial properties...\n"
|
||||||
|
|
||||||
|
stdcall guestprop_set, gp_key_os_product, gp_val_os_product
|
||||||
|
stdcall guestprop_set, gp_key_os_release, gp_val_os_release
|
||||||
|
stdcall guestprop_set, gp_key_version, gp_val_version
|
||||||
|
stdcall guestprop_set, gp_key_revision, gp_val_revision
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Initial properties set\n"
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; guestprop_set — Установить свойство (SET_PROP_VALUE, fn=4)
|
||||||
|
proc guestprop_set stdcall uses ebx ecx edx esi edi, name_ptr:dword, value_ptr:dword
|
||||||
|
cmp dword [gp_state.connected], 0
|
||||||
|
je .not_connected
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] SET '%s' = '%s'\n", [name_ptr], [value_ptr]
|
||||||
|
|
||||||
|
mov edi, [gp_state.parms_ptr]
|
||||||
|
|
||||||
|
; parm[0] = name (linaddr IN)
|
||||||
|
stdcall hgcm_strlen, [name_ptr]
|
||||||
|
inc eax ; +null
|
||||||
|
stdcall hgcm_param_linaddr_in, edi, [name_ptr], eax
|
||||||
|
add edi, sizeof.HGCM_PARM
|
||||||
|
|
||||||
|
; parm[1] = value (linaddr IN)
|
||||||
|
stdcall hgcm_strlen, [value_ptr]
|
||||||
|
inc eax ; +null
|
||||||
|
stdcall hgcm_param_linaddr_in, edi, [value_ptr], eax
|
||||||
|
|
||||||
|
; Вызвать HGCM
|
||||||
|
stdcall hgcm_call32_pagelist, [gp_state.client_id], GUEST_PROP_FN_SET_PROP_VALUE, [gp_state.parms_ptr], GUEST_PROP_SET_VALUE_PARM_COUNT
|
||||||
|
test eax, eax
|
||||||
|
jnz .failed
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] SET OK\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_connected:
|
||||||
|
mov eax, VERR_NOT_READY
|
||||||
|
ret
|
||||||
|
.failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] SET failed: 0x%x\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
; strlen(esi) → eax
|
||||||
|
endp
|
||||||
|
|
||||||
|
; guestprop_set_with_flags — Установить свойство с флагами (SET_PROP, fn=2)
|
||||||
|
proc guestprop_set_with_flags stdcall uses ebx ecx edx esi edi, name_ptr:dword, value_ptr:dword, flags_ptr:dword
|
||||||
|
cmp dword [gp_state.connected], 0
|
||||||
|
je .not_connected
|
||||||
|
|
||||||
|
mov edi, [gp_state.parms_ptr]
|
||||||
|
|
||||||
|
; parm[0] = name
|
||||||
|
stdcall hgcm_strlen, [name_ptr]
|
||||||
|
inc eax
|
||||||
|
stdcall hgcm_param_linaddr_in, edi, [name_ptr], eax
|
||||||
|
add edi, sizeof.HGCM_PARM
|
||||||
|
|
||||||
|
; parm[1] = value
|
||||||
|
stdcall hgcm_strlen, [value_ptr]
|
||||||
|
inc eax
|
||||||
|
stdcall hgcm_param_linaddr_in, edi, [value_ptr], eax
|
||||||
|
add edi, sizeof.HGCM_PARM
|
||||||
|
|
||||||
|
; parm[2] = flags
|
||||||
|
stdcall hgcm_strlen, [flags_ptr]
|
||||||
|
inc eax
|
||||||
|
stdcall hgcm_param_linaddr_in, edi, [flags_ptr], eax
|
||||||
|
|
||||||
|
stdcall hgcm_call32_pagelist, [gp_state.client_id], GUEST_PROP_FN_SET_PROP, [gp_state.parms_ptr], GUEST_PROP_SET_PARM_COUNT
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_connected:
|
||||||
|
mov eax, VERR_NOT_READY
|
||||||
|
ret
|
||||||
|
|
||||||
|
endp
|
||||||
|
|
||||||
|
proc hgcm_strlen uses esi, str_ptr:dword
|
||||||
|
mov esi, [str_ptr]
|
||||||
|
xor eax, eax
|
||||||
|
@@: cmp byte [esi], 0
|
||||||
|
je @f
|
||||||
|
inc esi
|
||||||
|
inc eax
|
||||||
|
jmp @b
|
||||||
|
@@: ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; guestprop_disable — Отключение сервиса
|
||||||
|
proc guestprop_disable uses edi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Disabling...\n"
|
||||||
|
|
||||||
|
cmp dword [gp_state.connected], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
stdcall hgcm_disconnect, [gp_state.client_id]
|
||||||
|
|
||||||
|
mov dword [gp_state.connected], 0
|
||||||
|
mov dword [gp_state.client_id], 0
|
||||||
|
|
||||||
|
; Освободить буферы
|
||||||
|
call guestprop_free_bufs
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [GuestProp] Disabled\n"
|
||||||
|
.done:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; guestprop_free_bufs — Освободить буферы Guest Properties
|
||||||
|
proc guestprop_free_bufs
|
||||||
|
cmp dword [gp_state.enum_buf_ptr], 0
|
||||||
|
je @f
|
||||||
|
invoke KernelFree, [gp_state.enum_buf_ptr]
|
||||||
|
mov dword [gp_state.enum_buf_ptr], 0
|
||||||
|
@@:
|
||||||
|
cmp dword [gp_state.small_bufs_ptr], 0
|
||||||
|
je @f
|
||||||
|
invoke KernelFree, [gp_state.small_bufs_ptr]
|
||||||
|
mov dword [gp_state.small_bufs_ptr], 0
|
||||||
|
; Обнулить указатели-потомки
|
||||||
|
mov dword [gp_state.parms_ptr], 0
|
||||||
|
mov dword [gp_state.name_buf_ptr], 0
|
||||||
|
mov dword [gp_state.value_buf_ptr], 0
|
||||||
|
mov dword [gp_state.flags_buf_ptr], 0
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
REGISTER_SERVICE svc_guestprop_name, GUEST_PROP_EVENT_MASK, GUEST_PROP_CAPS_MASK, \
|
||||||
|
guestprop_init, 0, guestprop_disable, 0, 0, AUTOSTART_GUEST_PROPS
|
||||||
243
services/heartbeat/heartbeat.inc
Normal file
243
services/heartbeat/heartbeat.inc
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Heartbeat Service
|
||||||
|
; Назначение : Периодическая отправка heartbeat сигналов хосту для мониторинга
|
||||||
|
; Файл : vmmdev/heartbeat.inc
|
||||||
|
; Протокол : Прямые VMMDev запросы (БЕЗ HGCM)
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
svc_heartbeat_name db "HEARTBEAT", 0
|
||||||
|
|
||||||
|
; --- Heartbeat Data ---
|
||||||
|
align 4
|
||||||
|
|
||||||
|
vbox_hb_last_send_time dd 0 ; timer_ticks последней отправки
|
||||||
|
vbox_hb_tick_interval dd 100
|
||||||
|
vbox_hb_enabled dd 0
|
||||||
|
|
||||||
|
; Статические буферы для запросов
|
||||||
|
align 4
|
||||||
|
vmmdev_heartbeat_configure_s VMMDEV_HEARTBEAT_CONFIGURE \
|
||||||
|
<sizeof.VMMDEV_HEARTBEAT_CONFIGURE, \
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION, \
|
||||||
|
VMMDEV_REQ_HEARTBEAT_CONFIGURE, \
|
||||||
|
0, 0, 0>, \
|
||||||
|
0, 0, 0; интервал и enabled заполняются при вызове
|
||||||
|
|
||||||
|
align 4
|
||||||
|
vmmdev_heartbeat_s VMMDEV_GUEST_HEARTBEAT \
|
||||||
|
<sizeof.VMMDEV_GUEST_HEARTBEAT, \
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION, \
|
||||||
|
VMMDEV_REQ_GUEST_HEARTBEAT, \
|
||||||
|
0, 0, 0>
|
||||||
|
|
||||||
|
; vmmdev_heartbeat_init — Инициализация heartbeat подсистемы
|
||||||
|
proc vmmdev_heartbeat_init
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Initializing...\n"
|
||||||
|
|
||||||
|
; Получить физические адреса буферов
|
||||||
|
mov eax, vmmdev_heartbeat_configure_s
|
||||||
|
mov [vbox_device.heartbeat_config_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.heartbeat_config_phys], eax
|
||||||
|
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Config packet: virt=0x%x, phys=0x%x\n", \
|
||||||
|
vmmdev_heartbeat_configure_s, eax
|
||||||
|
|
||||||
|
mov eax, vmmdev_heartbeat_s
|
||||||
|
mov [vbox_device.heartbeat_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.heartbeat_phys], eax
|
||||||
|
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Send packet : virt=0x%x, phys=0x%x\n", \
|
||||||
|
vmmdev_heartbeat_s, eax
|
||||||
|
|
||||||
|
; Запросить конфигурацию у хоста
|
||||||
|
call vmmdev_heartbeat_configure
|
||||||
|
test eax, eax
|
||||||
|
jnz .failed
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Initialized successfully\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Initialization failed: rc=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; vmmdev_heartbeat_configure — Запросить настройки heartbeat у хоста и включить мониторинг
|
||||||
|
proc vmmdev_heartbeat_configure uses ebx edi
|
||||||
|
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Configuring...\n"
|
||||||
|
|
||||||
|
mov edi, [vbox_device.heartbeat_config_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
; Заполнить запрос (header уже инициализирован статически)
|
||||||
|
; Обнулить rc перед отправкой
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc], 0
|
||||||
|
|
||||||
|
; Запросить у хоста интервал (0 = использовать дефолтный)
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval], 0
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval_high], 0
|
||||||
|
|
||||||
|
; Включить heartbeat
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.f_enabled], 1
|
||||||
|
|
||||||
|
; Отправить запрос
|
||||||
|
mov ebx, [vbox_device.heartbeat_config_phys]
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
; Проверить результат
|
||||||
|
mov eax, [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
js .error
|
||||||
|
|
||||||
|
; Получить интервал от хоста (если вернул)
|
||||||
|
mov eax, [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval]
|
||||||
|
mov edx, [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval_high]
|
||||||
|
|
||||||
|
; Конвертировать наносекунды в тики
|
||||||
|
; Предположим timer tick = 10ms = 10,000,000 ns
|
||||||
|
; Делим ns на 10,000,000 чтобы получить тики
|
||||||
|
test eax, eax
|
||||||
|
jz .use_default
|
||||||
|
test edx, edx
|
||||||
|
jnz .use_default ; Если интервал > 4.2 секунды, используем дефолт
|
||||||
|
|
||||||
|
; eax = наносекунды, конвертируем в тики (10ms)
|
||||||
|
; ticks = ns / 10,000,000
|
||||||
|
; правильное деление: 10,000,000 / 10,000 = 1000 (тики в 10ms)
|
||||||
|
mov ebx, 10000
|
||||||
|
xor edx, edx
|
||||||
|
div ebx ; eax = eax / 10000 (микросекунды)
|
||||||
|
mov ebx, 1000
|
||||||
|
xor edx, edx
|
||||||
|
div ebx ; eax = eax / 1000 (тики 10ms)
|
||||||
|
test eax, eax
|
||||||
|
jz .use_default
|
||||||
|
|
||||||
|
mov [vbox_hb_tick_interval], eax
|
||||||
|
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Interval set to %d ticks\n", eax
|
||||||
|
jmp .enabled
|
||||||
|
|
||||||
|
.use_default:
|
||||||
|
DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Using default interval: %d ticks\n", \
|
||||||
|
[vbox_hb_tick_interval]
|
||||||
|
|
||||||
|
.enabled:
|
||||||
|
; Включить heartbeat
|
||||||
|
mov dword [vbox_hb_enabled], 1
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Enabled\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Configure failed: rc=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Configure: bad pointer\n"
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; vmmdev_heartbeat_send — Отправить heartbeat хосту
|
||||||
|
proc vmmdev_heartbeat_send uses ebx edi
|
||||||
|
mov edi, [vbox_device.heartbeat_virt]
|
||||||
|
mov ebx, [vbox_device.heartbeat_phys]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
test ebx, ebx
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
; Обнулить rc перед отправкой
|
||||||
|
mov dword [edi + VMMDEV_GUEST_HEARTBEAT.header.rc], 0
|
||||||
|
|
||||||
|
; DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Sending...\n"
|
||||||
|
|
||||||
|
; Отправить запрос
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
; Проверить результат
|
||||||
|
mov eax, [edi + VMMDEV_GUEST_HEARTBEAT.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
js .error
|
||||||
|
|
||||||
|
; DEBUGF __DEBUG_HB__, "[VBoxGuest] [Heartbeat] Sent successfully\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Send failed: rc=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Send: bad pointer virt=0x%x phys=0x%x\n", edi, ebx
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
proc vmmdev_heartbeat_tick
|
||||||
|
cmp dword [vbox_hb_enabled], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
invoke GetTimerTicks ; eax = timer_ticks
|
||||||
|
sub eax, [vbox_hb_last_send_time]
|
||||||
|
cmp eax, [vbox_hb_tick_interval]
|
||||||
|
jb .done
|
||||||
|
|
||||||
|
invoke GetTimerTicks ; eax = текущее время для обновления last_send_time
|
||||||
|
mov [vbox_hb_last_send_time], eax
|
||||||
|
|
||||||
|
call vmmdev_heartbeat_send
|
||||||
|
test eax, eax
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Tick: send error 0x%x\n", eax
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; vmmdev_heartbeat_disable — Отключить heartbeat (при выгрузке драйвера)
|
||||||
|
proc vmmdev_heartbeat_disable uses ebx edi
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Disabling...\n"
|
||||||
|
|
||||||
|
; Выключить локальный флаг
|
||||||
|
mov dword [vbox_hb_enabled], 0
|
||||||
|
|
||||||
|
mov edi, [vbox_device.heartbeat_config_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
; Обнулить rc
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc], 0
|
||||||
|
|
||||||
|
; Отключить на хосте
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.f_enabled], 0
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval], 0
|
||||||
|
mov dword [edi + VMMDEV_HEARTBEAT_CONFIGURE.c_ns_interval_high], 0
|
||||||
|
|
||||||
|
; Отправить запрос
|
||||||
|
mov ebx, [vbox_device.heartbeat_config_phys]
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_HEARTBEAT_CONFIGURE.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
js .error
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Disabled\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Heartbeat] Disable failed: rc=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
REGISTER_SERVICE svc_heartbeat_name, 0, 0, \
|
||||||
|
vmmdev_heartbeat_init, 0, vmmdev_heartbeat_disable, 0, vmmdev_heartbeat_tick, AUTOSTART_HEARTBEAT
|
||||||
382
services/mouse/mouse.inc
Normal file
382
services/mouse/mouse.inc
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Mouse Service (Absolute Pointer)
|
||||||
|
; Назначение : Поддержка абсолютных координат мыши VirtualBox
|
||||||
|
; Файл : services/mouse/mouse.inc
|
||||||
|
;
|
||||||
|
; Использует расширенный протокол GetMouseStatusEx (type=223) если хост
|
||||||
|
; поддерживает FULL_STATE_PROTOCOL — кнопки и скролл приходят от VMMDev.
|
||||||
|
; Если хост не поддерживает — fallback на стандартный протокол + PS/2.
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
svc_mouse_name db "MOUSE", 0
|
||||||
|
|
||||||
|
; --- Mouse Data ---
|
||||||
|
align 4
|
||||||
|
vbox_mouse_enabled dd 0
|
||||||
|
vbox_mouse_x dd 0
|
||||||
|
vbox_mouse_y dd 0
|
||||||
|
vbox_mouse_buttons dd 0
|
||||||
|
vbox_mouse_dz dd 0 ; vertical scroll delta
|
||||||
|
vbox_mouse_dw dd 0 ; horizontal scroll delta
|
||||||
|
vbox_mouse_host_features dd 0
|
||||||
|
vbox_mouse_use_ext dd 0 ; 1 = extended protocol (buttons+scroll from VMMDev)
|
||||||
|
|
||||||
|
; Статический буфер для запроса (48 байт: 36 standard + 12 extended fields)
|
||||||
|
align 4
|
||||||
|
vmmdev_mouse_status_s VMMDEV_REQ_MOUSE_STATUS \
|
||||||
|
<sizeof.VMMDEV_REQ_MOUSE_STATUS, \
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION, \
|
||||||
|
VMMDEV_REQ_SET_MOUSE_STATUS, \
|
||||||
|
0, 0, VMMDEV_REQUESTOR_KERNEL>, \
|
||||||
|
VBOX_MOUSE_GUEST_FEATURES, 0, 0
|
||||||
|
; Extended fields (immediately after VMMDEV_REQ_MOUSE_STATUS in memory)
|
||||||
|
; VMMDev заполнит их при GET_MOUSE_STATUS_EX (type=223)
|
||||||
|
vmmdev_mouse_ext_dz dd 0 ; offset +36: vertical scroll
|
||||||
|
vmmdev_mouse_ext_dw dd 0 ; offset +40: horizontal scroll
|
||||||
|
vmmdev_mouse_ext_buttons dd 0 ; offset +44: button state
|
||||||
|
|
||||||
|
SIZEOF_MOUSE_STATUS_EX = sizeof.VMMDEV_REQ_MOUSE_STATUS + 12
|
||||||
|
MOUSE_EXT_OFS_DZ = sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||||||
|
MOUSE_EXT_OFS_DW = sizeof.VMMDEV_REQ_MOUSE_STATUS + 4
|
||||||
|
MOUSE_EXT_OFS_BUTTONS = sizeof.VMMDEV_REQ_MOUSE_STATUS + 8
|
||||||
|
|
||||||
|
; vmmdev_mouse_init — Инициализация подсистемы мыши
|
||||||
|
proc vmmdev_mouse_init
|
||||||
|
; Guard against double initialization (fn_init = fn_enable in REGISTER_SERVICE)
|
||||||
|
cmp dword [vbox_mouse_enabled], 1
|
||||||
|
je .already_initialized
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Initializing...\n"
|
||||||
|
|
||||||
|
; Получить физический адрес буфера
|
||||||
|
mov eax, vmmdev_mouse_status_s
|
||||||
|
mov [vbox_device.mouse_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.mouse_phys], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Buffer: virt=0x%x, phys=0x%x, ext_size=%d\n", \
|
||||||
|
vmmdev_mouse_status_s, eax, SIZEOF_MOUSE_STATUS_EX
|
||||||
|
|
||||||
|
; Запросить текущий статус (стандартный GET, т.к. use_ext=0)
|
||||||
|
call mouse_query_status
|
||||||
|
test eax, eax
|
||||||
|
jnz .failed
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Host features: 0x%x\n", [vbox_mouse_host_features]
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Host position: X=%d, Y=%d\n", [vbox_mouse_x], [vbox_mouse_y]
|
||||||
|
|
||||||
|
; Проверить поддержку расширенного протокола
|
||||||
|
test dword [vbox_mouse_host_features], VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL
|
||||||
|
jz .no_ext
|
||||||
|
|
||||||
|
; === Расширенный протокол ===
|
||||||
|
mov dword [vbox_mouse_use_ext], 1
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Host supports FULL_STATE_PROTOCOL — using extended mode\n"
|
||||||
|
|
||||||
|
call mouse_send_status
|
||||||
|
test eax, eax
|
||||||
|
jnz .failed
|
||||||
|
|
||||||
|
; Отключить PS/2 IRQ12 — VMMDev сам сообщает кнопки+скролл.
|
||||||
|
; Если PS/2 остаётся активным, SetMouseData(buttons=0) от VMMDev
|
||||||
|
; сбрасывает нажатые кнопки из PS/2 на каждом движении (glitch при drag).
|
||||||
|
stdcall i8042_mouse_control, 0
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] PS/2 IRQ12 disabled (buttons from VMMDev)\n"
|
||||||
|
|
||||||
|
jmp .ok
|
||||||
|
|
||||||
|
.no_ext:
|
||||||
|
; === Стандартный протокол (fallback) ===
|
||||||
|
mov dword [vbox_mouse_use_ext], 0
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] WARNING: Host does NOT support FULL_STATE_PROTOCOL\n"
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Fallback: standard protocol, PS/2 stays enabled for buttons\n"
|
||||||
|
|
||||||
|
call mouse_send_status
|
||||||
|
test eax, eax
|
||||||
|
jnz .failed
|
||||||
|
|
||||||
|
.ok:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Initialized OK (ext=%d)\n", [vbox_mouse_use_ext]
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.already_initialized:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Already initialized (skip reinit)\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Init failed: 0x%x\n", eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; mouse_query_status — Запросить статус у хоста
|
||||||
|
proc mouse_query_status
|
||||||
|
push ebx edi
|
||||||
|
|
||||||
|
mov edi, [vbox_device.mouse_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
; Очистить rc
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
|
||||||
|
|
||||||
|
; Выбрать тип запроса
|
||||||
|
cmp dword [vbox_mouse_use_ext], 0
|
||||||
|
je .setup_standard
|
||||||
|
|
||||||
|
; === Extended GET (type=223, size=48) ===
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], SIZEOF_MOUSE_STATUS_EX
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_GET_MOUSE_STATUS_EX
|
||||||
|
jmp .send
|
||||||
|
|
||||||
|
.setup_standard:
|
||||||
|
; === Standard GET (type=1, size=36) ===
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_GET_MOUSE_STATUS
|
||||||
|
|
||||||
|
.send:
|
||||||
|
mov ebx, [vbox_device.mouse_phys]
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
js .error
|
||||||
|
|
||||||
|
; === Сохранить общие данные ===
|
||||||
|
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features]
|
||||||
|
mov [vbox_mouse_host_features], eax
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_x_pos]
|
||||||
|
mov [vbox_mouse_x], eax
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_y_pos]
|
||||||
|
mov [vbox_mouse_y], eax
|
||||||
|
|
||||||
|
; === Если extended — сохранить кнопки и скролл ===
|
||||||
|
cmp dword [vbox_mouse_use_ext], 0
|
||||||
|
je .no_ext
|
||||||
|
|
||||||
|
mov eax, [edi + MOUSE_EXT_OFS_BUTTONS]
|
||||||
|
and eax, 0x1F ; биты 0-4: left, right, middle, X1, X2
|
||||||
|
mov [vbox_mouse_buttons], eax
|
||||||
|
|
||||||
|
mov eax, [edi + MOUSE_EXT_OFS_DZ]
|
||||||
|
mov [vbox_mouse_dz], eax
|
||||||
|
|
||||||
|
mov eax, [edi + MOUSE_EXT_OFS_DW]
|
||||||
|
mov [vbox_mouse_dw], eax
|
||||||
|
|
||||||
|
.no_ext:
|
||||||
|
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] VMMDev: feat=0x%x, X=%d, Y=%d, btn=0x%x, dz=%d\n", \
|
||||||
|
[vbox_mouse_host_features], [vbox_mouse_x], [vbox_mouse_y], \
|
||||||
|
[vbox_mouse_buttons], [vbox_mouse_dz]
|
||||||
|
|
||||||
|
; Восстановить header для SET запросов
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_SET_MOUSE_STATUS
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
pop edi ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Query failed: rc=0x%x\n", eax
|
||||||
|
; Восстановить header даже при ошибке
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.size], sizeof.VMMDEV_REQ_MOUSE_STATUS
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.request_type], VMMDEV_REQ_SET_MOUSE_STATUS
|
||||||
|
pop edi ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
pop edi ebx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; mouse_send_status — Отправить guest capabilities хосту
|
||||||
|
proc mouse_send_status
|
||||||
|
push ebx edi
|
||||||
|
|
||||||
|
mov edi, [vbox_device.mouse_virt]
|
||||||
|
mov ebx, [vbox_device.mouse_phys]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
test ebx, ebx
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_x_pos], 0
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.pointer_y_pos], 0
|
||||||
|
|
||||||
|
; Выбрать guest features
|
||||||
|
cmp dword [vbox_mouse_use_ext], 0
|
||||||
|
je .std_feats
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], VBOX_MOUSE_GUEST_FEATURES_EXT
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Sending guest features: 0x%x (extended)\n", VBOX_MOUSE_GUEST_FEATURES_EXT
|
||||||
|
jmp .send
|
||||||
|
.std_feats:
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], VBOX_MOUSE_GUEST_FEATURES
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Sending guest features: 0x%x (standard)\n", VBOX_MOUSE_GUEST_FEATURES
|
||||||
|
.send:
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
js .error
|
||||||
|
|
||||||
|
mov dword [vbox_mouse_enabled], 1
|
||||||
|
xor eax, eax
|
||||||
|
pop edi ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Send status failed: 0x%x\n", eax
|
||||||
|
pop edi ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
pop edi ebx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; mouse_handle_event — Обработка событий мыши (вызывается из IRQ)
|
||||||
|
proc mouse_handle_event
|
||||||
|
push eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] Event: mask=0x%x\n", eax
|
||||||
|
|
||||||
|
; Любое из двух событий — нужен свежий статус
|
||||||
|
call mouse_query_status
|
||||||
|
test eax, eax
|
||||||
|
jnz .query_fail
|
||||||
|
|
||||||
|
pop eax
|
||||||
|
push eax
|
||||||
|
|
||||||
|
; Для position changed — обновить координаты в системе
|
||||||
|
test eax, VMMDEV_EVENT_MOUSE_POSITION_CHANGED
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
call mouse_update_system_position
|
||||||
|
jmp .done
|
||||||
|
|
||||||
|
.query_fail:
|
||||||
|
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] Query failed in event handler: 0x%x\n", eax
|
||||||
|
|
||||||
|
.done:
|
||||||
|
pop eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; mouse_update_system_position — Отправить позицию + кнопки + скролл в KolibriOS
|
||||||
|
proc mouse_update_system_position
|
||||||
|
push eax ebx ecx edx
|
||||||
|
|
||||||
|
; VirtualBox: 0-65535, KolibriOS: 0-32767
|
||||||
|
mov ecx, [vbox_mouse_x]
|
||||||
|
shr ecx, 1 ; X / 2
|
||||||
|
|
||||||
|
mov edx, [vbox_mouse_y]
|
||||||
|
shr edx, 1 ; Y / 2
|
||||||
|
|
||||||
|
; Флаги: bit31=X_abs, bit30=Y_abs + кнопки (bits 0-4)
|
||||||
|
mov eax, 0xC0000000
|
||||||
|
or eax, [vbox_mouse_buttons]
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_MOUSE__, "[VBoxGuest] [Mouse] SetMouseData: flags=0x%x, X=%d, Y=%d, btn=0x%x, dz=%d, dw=%d\n", \
|
||||||
|
eax, ecx, edx, [vbox_mouse_buttons], [vbox_mouse_dz], [vbox_mouse_dw]
|
||||||
|
|
||||||
|
invoke SetMouseData, eax, ecx, edx, [vbox_mouse_dz], [vbox_mouse_dw]
|
||||||
|
|
||||||
|
; Сбросить дельты скролла (это дельты, не состояние)
|
||||||
|
mov dword [vbox_mouse_dz], 0
|
||||||
|
mov dword [vbox_mouse_dw], 0
|
||||||
|
|
||||||
|
pop edx ecx ebx eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; mouse_disable — Отключить мышь
|
||||||
|
proc mouse_disable
|
||||||
|
push ebx edi
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Mouse] Disabling...\n"
|
||||||
|
|
||||||
|
mov dword [vbox_mouse_enabled], 0
|
||||||
|
|
||||||
|
mov edi, [vbox_device.mouse_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.header.rc], 0
|
||||||
|
mov dword [edi + VMMDEV_REQ_MOUSE_STATUS.mouse_features], 0
|
||||||
|
|
||||||
|
mov ebx, [vbox_device.mouse_phys]
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
; Восстановить PS/2 при выключении
|
||||||
|
stdcall i8042_mouse_control, 1
|
||||||
|
|
||||||
|
.done:
|
||||||
|
xor eax, eax
|
||||||
|
pop edi ebx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
; i8042_mouse_control — Включить/выключить IRQ12 PS/2 мыши
|
||||||
|
proc i8042_mouse_control stdcall, enable:dword
|
||||||
|
call .wait_input
|
||||||
|
mov al, 0x20
|
||||||
|
out 0x64, al
|
||||||
|
|
||||||
|
call .wait_output
|
||||||
|
in al, 0x60
|
||||||
|
|
||||||
|
and al, not 0x02
|
||||||
|
cmp [enable], 0
|
||||||
|
je @f
|
||||||
|
or al, 0x02
|
||||||
|
@@:
|
||||||
|
mov ah, al
|
||||||
|
|
||||||
|
call .wait_input
|
||||||
|
mov al, 0x60
|
||||||
|
out 0x64, al
|
||||||
|
|
||||||
|
call .wait_input
|
||||||
|
mov al, ah
|
||||||
|
out 0x60, al
|
||||||
|
ret
|
||||||
|
|
||||||
|
.wait_input:
|
||||||
|
push ecx
|
||||||
|
mov ecx, 0xFFFF
|
||||||
|
@@:
|
||||||
|
in al, 0x64
|
||||||
|
test al, 0x02
|
||||||
|
jz @f
|
||||||
|
dec ecx
|
||||||
|
jnz @b
|
||||||
|
@@:
|
||||||
|
pop ecx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.wait_output:
|
||||||
|
push ecx
|
||||||
|
mov ecx, 0xFFFF
|
||||||
|
@@:
|
||||||
|
in al, 0x64
|
||||||
|
test al, 0x01
|
||||||
|
jnz @f
|
||||||
|
dec ecx
|
||||||
|
jnz @b
|
||||||
|
@@:
|
||||||
|
pop ecx
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
REGISTER_SERVICE svc_mouse_name, MOUSE_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_MOUSE, \
|
||||||
|
vmmdev_mouse_init, vmmdev_mouse_init, mouse_disable, mouse_handle_event, 0, AUTOSTART_MOUSE
|
||||||
265
services/seamless/seamless.inc
Normal file
265
services/seamless/seamless.inc
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Seamless Mode Service
|
||||||
|
; Назначение : Получение запросов на seamless mode от хоста
|
||||||
|
; Файл : services/seamless/seamless.inc
|
||||||
|
; Протокол : VMMDevReq_GetSeamlessChangeRequest (73)
|
||||||
|
;
|
||||||
|
; Seamless mode = «бесшовные окна»: окна гостя рисуются как окна хоста,
|
||||||
|
; без рамки VM. Хост периодически спрашивает гостя: «какие области экрана
|
||||||
|
; заняты окнами?» (visible region).
|
||||||
|
;
|
||||||
|
; Для KolibriOS полный seamless пока невозможен (нет API получения
|
||||||
|
; регионов окон), но мы:
|
||||||
|
; 1. Сообщаем хосту VMMDEV_GUEST_SUPPORTS_SEAMLESS → кнопка
|
||||||
|
; «View → Seamless Mode» в VBox GUI станет активной
|
||||||
|
; 2. Получаем события SEAMLESS_MODE_CHANGE через IRQ
|
||||||
|
; 3. Запрашиваем текущий режим через GetSeamlessChangeRequest
|
||||||
|
; 4. При включении seamless → отправляем visible region = весь экран
|
||||||
|
; (один прямоугольник 0,0 → width,height)
|
||||||
|
;
|
||||||
|
; Архитектура (как display):
|
||||||
|
; IRQ event → seamless_handle_event → pending=1, timestamp
|
||||||
|
; Timer tick → seamless_tick → если pending и debounce прошёл → обработать
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
svc_seamless_name db "SEAMLESS", 0
|
||||||
|
|
||||||
|
; --- Data ---
|
||||||
|
align 4
|
||||||
|
vbox_seamless_enabled dd 0
|
||||||
|
vbox_seamless_current_mode dd 0 ; VMMDEV_SEAMLESS_*
|
||||||
|
vbox_seamless_pending dd 0
|
||||||
|
vbox_seamless_event_time dd 0
|
||||||
|
|
||||||
|
; Debounce: 500ms (50 тиков * 10ms)
|
||||||
|
SEAMLESS_DELAY_TICKS equ 50
|
||||||
|
|
||||||
|
; --- Пакет GetSeamlessChangeRequest ---
|
||||||
|
align 4
|
||||||
|
vmmdev_seamless_change_s VMMDEV_SEAMLESS_CHANGE_REQUEST \
|
||||||
|
<sizeof.VMMDEV_SEAMLESS_CHANGE_REQUEST, \
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION, \
|
||||||
|
VMMDEV_REQ_GET_SEAMLESS_CHANGE, \
|
||||||
|
0, 0, 0>, \
|
||||||
|
0, \
|
||||||
|
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE ; eventAck
|
||||||
|
|
||||||
|
align 4
|
||||||
|
seamless_change_virt dd 0
|
||||||
|
seamless_change_phys dd 0
|
||||||
|
|
||||||
|
; --- Пакет VideoSetVisibleRegion (1 прямоугольник) ---
|
||||||
|
align 4
|
||||||
|
vmmdev_visible_region_s VMMDEV_VIDEO_SET_VISIBLE_REGION \
|
||||||
|
<sizeof.VMMDEV_VIDEO_SET_VISIBLE_REGION, \
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION, \
|
||||||
|
VMMDEV_REQ_VIDEO_SET_VISIBLE_REGION, \
|
||||||
|
0, 0, 0>, \
|
||||||
|
1, \
|
||||||
|
<0, 0, 0, 0> ; rect0: заполняется при вызове
|
||||||
|
|
||||||
|
align 4
|
||||||
|
visible_region_virt dd 0
|
||||||
|
visible_region_phys dd 0
|
||||||
|
|
||||||
|
; seamless_init — Инициализация
|
||||||
|
proc seamless_init uses ebx
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Initializing...\n"
|
||||||
|
|
||||||
|
; --- Физические адреса пакетов ---
|
||||||
|
mov eax, vmmdev_seamless_change_s
|
||||||
|
mov [seamless_change_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
test eax, eax
|
||||||
|
jz .phys_failed
|
||||||
|
mov [seamless_change_phys], eax
|
||||||
|
|
||||||
|
mov eax, vmmdev_visible_region_s
|
||||||
|
mov [visible_region_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
test eax, eax
|
||||||
|
jz .phys_failed
|
||||||
|
mov [visible_region_phys], eax
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] SeamlessChange pkt: virt=0x%x phys=0x%x\n", \
|
||||||
|
[seamless_change_virt], [seamless_change_phys]
|
||||||
|
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] VisibleRegion pkt: virt=0x%x phys=0x%x\n", \
|
||||||
|
[visible_region_virt], [visible_region_phys]
|
||||||
|
|
||||||
|
mov dword [vbox_seamless_enabled], 1
|
||||||
|
mov dword [vbox_seamless_current_mode], VMMDEV_SEAMLESS_DISABLED
|
||||||
|
|
||||||
|
; Сразу запросить текущий режим (может быть уже включен)
|
||||||
|
call seamless_query_mode
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Initialized OK (mode=%d)\n", \
|
||||||
|
[vbox_seamless_current_mode]
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.phys_failed:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] GetPhysAddr failed\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; seamless_handle_event — Из IRQ контекста
|
||||||
|
proc seamless_handle_event
|
||||||
|
test eax, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE
|
||||||
|
jz .done
|
||||||
|
cmp dword [vbox_seamless_enabled], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
invoke GetTimerTicks
|
||||||
|
mov [vbox_seamless_event_time], eax
|
||||||
|
mov dword [vbox_seamless_pending], 1
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] Event queued\n"
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; seamless_tick — Из таймера, debounce
|
||||||
|
proc seamless_tick
|
||||||
|
cmp dword [vbox_seamless_pending], 0
|
||||||
|
je .done
|
||||||
|
|
||||||
|
invoke GetTimerTicks
|
||||||
|
sub eax, [vbox_seamless_event_time]
|
||||||
|
cmp eax, SEAMLESS_DELAY_TICKS
|
||||||
|
jb .done
|
||||||
|
|
||||||
|
mov dword [vbox_seamless_pending], 0
|
||||||
|
|
||||||
|
DEBUGF __DEBUG_SEAMLESS__, "[VBoxGuest] [Seamless] Processing mode change...\n"
|
||||||
|
call seamless_query_mode
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; seamless_query_mode — Запросить текущий seamless mode у хоста (req 73)
|
||||||
|
proc seamless_query_mode uses ebx edi
|
||||||
|
mov edi, [seamless_change_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
; Заполнить запрос
|
||||||
|
mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.header.rc], 0
|
||||||
|
mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.mode], 0
|
||||||
|
mov dword [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.eventAck], \
|
||||||
|
VMMDEV_EVENT_SEAMLESS_MODE_CHANGE
|
||||||
|
|
||||||
|
; Отправить
|
||||||
|
stdcall vmmdev_send_request, [seamless_change_phys]
|
||||||
|
|
||||||
|
; Проверить rc
|
||||||
|
mov eax, [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
; Прочитать режим
|
||||||
|
mov eax, [edi + VMMDEV_SEAMLESS_CHANGE_REQUEST.mode]
|
||||||
|
mov ebx, [vbox_seamless_current_mode]
|
||||||
|
mov [vbox_seamless_current_mode], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Mode: %d → %d\n", ebx, eax
|
||||||
|
|
||||||
|
; Обработать смену режима
|
||||||
|
cmp eax, VMMDEV_SEAMLESS_DISABLED
|
||||||
|
je .mode_disabled
|
||||||
|
|
||||||
|
cmp eax, VMMDEV_SEAMLESS_VISIBLE_REGION
|
||||||
|
je .mode_visible_region
|
||||||
|
|
||||||
|
cmp eax, VMMDEV_SEAMLESS_HOST_WINDOW
|
||||||
|
je .mode_host_window
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Unknown mode: %d\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.mode_disabled:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Seamless DISABLED\n"
|
||||||
|
ret
|
||||||
|
|
||||||
|
.mode_visible_region:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Seamless ENABLED (visible region)\n"
|
||||||
|
; Отправить видимую область = весь экран
|
||||||
|
call seamless_send_fullscreen_region
|
||||||
|
ret
|
||||||
|
|
||||||
|
.mode_host_window:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Seamless ENABLED (host window)\n"
|
||||||
|
call seamless_send_fullscreen_region
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Bad pointer\n"
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] GetSeamlessChange failed: rc=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; seamless_send_fullscreen_region — Отправить visible region = весь экран
|
||||||
|
proc seamless_send_fullscreen_region uses ebx edi
|
||||||
|
mov edi, [visible_region_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
; Получить текущее разрешение из KOS
|
||||||
|
invoke GetDisplay
|
||||||
|
test eax, eax
|
||||||
|
jz .no_display
|
||||||
|
|
||||||
|
mov ebx, eax
|
||||||
|
mov ecx, [ebx + DISPLAY.width]
|
||||||
|
mov edx, [ebx + DISPLAY.height]
|
||||||
|
|
||||||
|
DEBUGF 1, "[VBoxGuest] [Seamless] Sending region: 0,0 → %d,%d\n", ecx, edx
|
||||||
|
|
||||||
|
; Заполнить заголовок
|
||||||
|
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.header.rc], 0
|
||||||
|
|
||||||
|
; Заполнить прямоугольник
|
||||||
|
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.cRect], 1
|
||||||
|
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.xLeft], 0
|
||||||
|
mov dword [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.yTop], 0
|
||||||
|
mov [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.xRight], ecx
|
||||||
|
mov [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.rect0.yBottom], edx
|
||||||
|
|
||||||
|
; Отправить
|
||||||
|
stdcall vmmdev_send_request, [visible_region_phys]
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_VIDEO_SET_VISIBLE_REGION.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
DEBUGF 1, "[VBoxGuest] [Seamless] Region sent OK\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Bad pointer\n"
|
||||||
|
ret
|
||||||
|
.no_display:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] No display\n"
|
||||||
|
ret
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] SetVisibleRegion failed: rc=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; seamless_disable — Выключить seamless
|
||||||
|
proc seamless_disable
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Seamless] Disabling...\n"
|
||||||
|
mov dword [vbox_seamless_enabled], 0
|
||||||
|
mov dword [vbox_seamless_pending], 0
|
||||||
|
mov dword [vbox_seamless_current_mode], VMMDEV_SEAMLESS_DISABLED
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
REGISTER_SERVICE svc_seamless_name, SEAMLESS_EVENT_MASK, VMMDEV_GUEST_SUPPORTS_SEAMLESS, \
|
||||||
|
seamless_init, 0, seamless_disable, seamless_handle_event, seamless_tick, AUTOSTART_SEAMLESS
|
||||||
11
services/services.inc
Normal file
11
services/services.inc
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Services Aggregator
|
||||||
|
; =============================================================================
|
||||||
|
include 'services/mouse/mouse.inc'
|
||||||
|
include 'services/heartbeat/heartbeat.inc'
|
||||||
|
include 'services/guest_props/guest_props.inc'
|
||||||
|
include 'services/display/display.inc'
|
||||||
|
include 'services/shared_folders/shared_folders.inc'
|
||||||
|
include 'services/clipboard/clipboard.inc'
|
||||||
|
include 'services/seamless/seamless.inc'
|
||||||
|
include 'services/timesync/timesync.inc'
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
; =============================================================================
|
|
||||||
; SharedFolder Service - FIXED VERSION
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
VBOX_SHAREDFOLDER_SERVICE db 'VBoxSharedFolders', 0
|
|
||||||
|
|
||||||
align 4
|
|
||||||
sf_state SF_STATE
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: sf_init
|
|
||||||
; =============================================================================
|
|
||||||
proc sf_init uses ebx ecx edx esi edi
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
DEBUGF 1, "[SF] >>> SharedFolder initialization STARTED\n"
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
|
|
||||||
mov ax, [vbox_device.port]
|
|
||||||
test ax, ax
|
|
||||||
jz .vmmdev_not_ready
|
|
||||||
|
|
||||||
mov eax, [vbox_device.mmio]
|
|
||||||
test eax, eax
|
|
||||||
jz .vmmdev_not_ready
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] VMMDev ready: port=0x%x, mmio=0x%x\n", \
|
|
||||||
[vbox_device.port], [vbox_device.mmio]
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] Connecting to SharedFolders HGCM service...\n"
|
|
||||||
|
|
||||||
stdcall hgcm_connect, VBOX_SHAREDFOLDER_SERVICE
|
|
||||||
test eax, eax
|
|
||||||
jz .connect_failed
|
|
||||||
|
|
||||||
mov [sf_state.client_id], eax
|
|
||||||
mov dword [sf_state.connected], 1
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] *** HGCM CONNECTED *** client_id=0x%x\n", eax
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] Querying available shared folders...\n"
|
|
||||||
|
|
||||||
call sf_query_mappings
|
|
||||||
DEBUGF 1, "[SF] After sf_query_mappings, eax=0x%x\n", eax
|
|
||||||
test eax, eax
|
|
||||||
jnz .query_failed
|
|
||||||
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
DEBUGF 1, "[SF] *** SHAREDFOLDER INITIALIZED SUCCESSFULLY ***\n"
|
|
||||||
DEBUGF 1, "[SF] client_id : 0x%x\n", [sf_state.client_id]
|
|
||||||
DEBUGF 1, "[SF] folder_count : %d\n", [sf_state.folder_count]
|
|
||||||
DEBUGF 1, "=================================================\n"
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.vmmdev_not_ready:
|
|
||||||
DEBUGF 1, "[SF] ERROR: VMMDev not initialized!\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.connect_failed:
|
|
||||||
DEBUGF 1, "[SF] *** CRITICAL: HGCM connect failed ***\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.query_failed:
|
|
||||||
DEBUGF 1, "[SF] WARNING: Query mappings failed (non-fatal)\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: sf_query_mappings
|
|
||||||
; ПОЛНОСТЬЮ ИСПРАВЛЕННАЯ ВЕРСИЯ
|
|
||||||
; =============================================================================
|
|
||||||
proc sf_query_mappings uses ebx esi edi
|
|
||||||
DEBUGF 1, "[SF] ========================================\n"
|
|
||||||
DEBUGF 1, "[SF] >>> sf_query_mappings STARTED\n"
|
|
||||||
DEBUGF 1, "[SF] ========================================\n"
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; ШАГ 1: Получить физические адреса
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 1, "[SF] STEP 1: Buffer preparation\n"
|
|
||||||
|
|
||||||
; Получить физический адрес буфера
|
|
||||||
mov eax, sf_mappings_buffer
|
|
||||||
invoke GetPhysAddr
|
|
||||||
mov ebx, eax ; ebx = buffer_phys
|
|
||||||
DEBUGF 1, "[SF] buffer_virt = 0x%x\n", sf_mappings_buffer
|
|
||||||
DEBUGF 1, "[SF] buffer_phys = 0x%x\n", ebx
|
|
||||||
|
|
||||||
; Вычислить PFN (Page Frame Number)
|
|
||||||
mov eax, ebx
|
|
||||||
shr eax, PAGE_SHIFT
|
|
||||||
mov [sf_pagelist_pfn], eax
|
|
||||||
DEBUGF 1, "[SF] buffer_pfn = 0x%x\n", eax
|
|
||||||
|
|
||||||
; Получить физический адрес pagelist структуры
|
|
||||||
mov eax, sf_pagelist
|
|
||||||
invoke GetPhysAddr
|
|
||||||
mov esi, eax ; esi = pagelist_phys
|
|
||||||
DEBUGF 1, "[SF] pagelist_phys = 0x%x\n", esi
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; ШАГ 2: Формирование HGCM пакета (56 байт как в Clipboard!)
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 1, "[SF] STEP 2: Building HGCM packet (56 bytes)\n"
|
|
||||||
|
|
||||||
; Используем пакет на 56 байт (как в работающем Clipboard запросе)
|
|
||||||
mov edi, sf_query_mappings_pkt
|
|
||||||
|
|
||||||
; Очистка пакета (56 байт)
|
|
||||||
push edi
|
|
||||||
mov ecx, 56/4
|
|
||||||
xor eax, eax
|
|
||||||
rep stosd
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; ВАЖНО: Сравниваем с работающим Clipboard запросом!
|
|
||||||
; Clipboard пакет (56 байт):
|
|
||||||
; 0x38 0x00 0x00 0x00 0x01 0x00 0x01 0x00 0x3E 0x00 0x00 0x00 0x1F 0xFF 0xFF 0xFF
|
|
||||||
; 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x1F 0xFF 0xFF 0xFF
|
|
||||||
; 0x03 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x01 0x00 0x00 0x00
|
|
||||||
; =========================================================================
|
|
||||||
|
|
||||||
; Заполняем пакет ТОЧНО как в Clipboard запросе, но с нашими параметрами
|
|
||||||
|
|
||||||
; VMMDev Request Header (первые 24 байта)
|
|
||||||
mov dword [edi + 0], 56 ; size = 56 байт (0x38) как в Clipboard!
|
|
||||||
mov dword [edi + 4], VMMDEV_REQUEST_HEADER_VERSION
|
|
||||||
mov dword [edi + 8], VMMDEV_HGCM_CALL32
|
|
||||||
|
|
||||||
; КРИТИЧНО: rc должен быть 0x1FFFFFFF (как в работающем запросе)!
|
|
||||||
mov dword [edi + 12], 0x1FFFFFFF ; rc = VERR_HGCM_ASYNC_EXECUTE
|
|
||||||
; mov dword [edi + 12], 0
|
|
||||||
|
|
||||||
mov dword [edi + 16], 0 ; reserved1
|
|
||||||
mov dword [edi + 20], 0 ; reserved2
|
|
||||||
|
|
||||||
; HGCM Call Header (смещение 24)
|
|
||||||
mov eax, [sf_state.client_id]
|
|
||||||
mov dword [edi + 24], eax ; clientID
|
|
||||||
mov dword [edi + 28], SHFL_FN_QUERY_MAPPINGS ; function
|
|
||||||
mov dword [edi + 32], 3 ; cParms = 3 параметра
|
|
||||||
|
|
||||||
; КРИТИЧНО: Еще одно поле rc в HGCM header (смещение 36)!
|
|
||||||
mov dword [edi + 36], 0x1FFFFFFF ; Еще один rc (как в Clipboard)
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; Параметры (начинаются с 40 байта)
|
|
||||||
; В 56-байтовом пакете у нас есть место для 3 параметров по 4+4 байта каждый
|
|
||||||
; =========================================================================
|
|
||||||
|
|
||||||
; Параметр 0: flags (U32, IN) - смещение 40
|
|
||||||
mov dword [edi + 40], HGCM_PARM_TYPE_32BIT ; type = 1
|
|
||||||
mov dword [edi + 44], 0 ; flags = 0
|
|
||||||
|
|
||||||
; Параметр 1: count (U32, OUT) - смещение 44+4=48
|
|
||||||
mov dword [edi + 48], HGCM_PARM_TYPE_32BIT ; type = 1
|
|
||||||
mov dword [edi + 52], 0 ; начальное значение = 0
|
|
||||||
|
|
||||||
; Параметр 2: buffer (PAGELIST, OUT) - смещение 48+8=56
|
|
||||||
; Но у нас пакет всего 56 байт! Значит, параметры идут последовательно.
|
|
||||||
; Исправляем: параметр 2 начинается с 52+4=56, но это уже за пределами 56 байт!
|
|
||||||
|
|
||||||
; ВНИМАНИЕ: В пакете Clipboard параметры идут последовательно без пропусков:
|
|
||||||
; offset 40: param0.type
|
|
||||||
; offset 44: param0.value
|
|
||||||
; offset 48: param1.type
|
|
||||||
; offset 52: param1.value
|
|
||||||
; offset 56: param2.type - но это уже за пределами 56 байт!
|
|
||||||
|
|
||||||
; Значит, пакет должен быть больше 56 байт!
|
|
||||||
; Попробуем 80 байт, как было раньше, но с правильными смещениями
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; ВЕРСИЯ 2: Пакет на 80 байт с правильными смещениями
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 1, "[SF] STEP 2a: Rebuilding packet (80 bytes with correct offsets)\n"
|
|
||||||
|
|
||||||
; Переопределяем пакет на 80 байт
|
|
||||||
mov dword [edi + 0], 80 ; size = 80 байт
|
|
||||||
|
|
||||||
; Очищаем оставшуюся часть пакета
|
|
||||||
push edi
|
|
||||||
add edi, 56
|
|
||||||
mov ecx, (80-56)/4
|
|
||||||
xor eax, eax
|
|
||||||
rep stosd
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
; Теперь параметр 2 должен начинаться с 56 байта
|
|
||||||
mov dword [edi + 56], HGCM_PARM_TYPE_PAGELIST ; type = 8
|
|
||||||
mov dword [edi + 60], 1 ; cPages = 1
|
|
||||||
mov dword [edi + 64], esi ; paPages = физический адрес pagelist
|
|
||||||
|
|
||||||
; Padding для 80-байтового пакета
|
|
||||||
mov dword [edi + 68], 0
|
|
||||||
mov dword [edi + 72], 0
|
|
||||||
mov dword [edi + 76], 0
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] Packet structure (80 bytes):\n"
|
|
||||||
DEBUGF 1, "[SF] size: 80 (0x50)\n"
|
|
||||||
DEBUGF 1, "[SF] rc: 0x%x\n", [edi + 12]
|
|
||||||
DEBUGF 1, "[SF] clientID: 0x%x\n", [sf_state.client_id]
|
|
||||||
DEBUGF 1, "[SF] function: %d\n", SHFL_FN_QUERY_MAPPINGS
|
|
||||||
DEBUGF 1, "[SF] cParms: ?\n"
|
|
||||||
DEBUGF 1, "[SF] param0: type=1, value=0\n"
|
|
||||||
DEBUGF 1, "[SF] param1: type=1, value=0\n"
|
|
||||||
DEBUGF 1, "[SF] param2: type=8, cPages=?, paPages=0x%x\n", esi
|
|
||||||
|
|
||||||
; Дамп пакета для проверки
|
|
||||||
LOG_DUMP_MEMORY edi, 80
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; ШАГ 3: Отправка запроса
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 1, "[SF] STEP 3: Sending HGCM request...\n"
|
|
||||||
|
|
||||||
stdcall hgcm_send_request, edi
|
|
||||||
DEBUGF 1, "[SF] hgcm_send_request returned: 0x%x\n", eax
|
|
||||||
|
|
||||||
test eax, eax
|
|
||||||
jnz .request_failed
|
|
||||||
|
|
||||||
; =========================================================================
|
|
||||||
; ШАГ 4: Анализ результата
|
|
||||||
; =========================================================================
|
|
||||||
DEBUGF 1, "[SF] STEP 4: Analyzing response\n"
|
|
||||||
|
|
||||||
; Проверка кода возврата VMMDev
|
|
||||||
mov eax, [edi + 12]
|
|
||||||
DEBUGF 1, "[SF] VMMDev RC = 0x%x\n", eax
|
|
||||||
|
|
||||||
; Проверка результата HGCM
|
|
||||||
mov eax, [edi + 36] ; HGCM result code (смещение 36 в 80-байтовом пакете)
|
|
||||||
DEBUGF 1, "[SF] HGCM result = 0x%x\n", eax
|
|
||||||
|
|
||||||
; Проверяем оба кода
|
|
||||||
cmp dword [edi + 12], 0x8000B57 ; VERR_HGCM_ASYNC_EXECUTE
|
|
||||||
je .async_ok
|
|
||||||
|
|
||||||
cmp dword [edi + 12], 0
|
|
||||||
je .check_hgcm
|
|
||||||
|
|
||||||
; Ошибка VMMDev
|
|
||||||
DEBUGF 1, "[SF] VMMDev error: 0x%x\n", [edi + 12]
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.check_hgcm:
|
|
||||||
cmp dword [edi + 36], 0
|
|
||||||
je .success
|
|
||||||
|
|
||||||
; Ошибка HGCM
|
|
||||||
DEBUGF 1, "[SF] HGCM error: 0x%x\n", [edi + 36]
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.async_ok:
|
|
||||||
; Асинхронное выполнение - проверяем HGCM результат
|
|
||||||
cmp dword [edi + 36], 0
|
|
||||||
je .success
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] HGCM async error: 0x%x\n", [edi + 36]
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.success:
|
|
||||||
; Успех - получаем количество папок из param1.value
|
|
||||||
mov eax, [edi + 52] ; param1.value (count) - смещение 52
|
|
||||||
mov [sf_state.folder_count], eax
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] ========================================\n"
|
|
||||||
DEBUGF 1, "[SF] SUCCESS: Found %d shared folder(s)\n", eax
|
|
||||||
DEBUGF 1, "[SF] ========================================\n"
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.request_failed:
|
|
||||||
DEBUGF 1, "[SF] ERROR: hgcm_send_request failed (rc=0x%x)\n", eax
|
|
||||||
DEBUGF 1, "[SF] Check packet structure and sizes\n"
|
|
||||||
mov eax, -1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ПРОЦЕДУРА: sf_shutdown
|
|
||||||
; =============================================================================
|
|
||||||
proc sf_shutdown uses eax
|
|
||||||
DEBUGF 1, "[SF] >>> Shutting down SharedFolder\n"
|
|
||||||
|
|
||||||
cmp dword [sf_state.connected], 1
|
|
||||||
jne .not_connected
|
|
||||||
|
|
||||||
stdcall hgcm_disconnect, [sf_state.client_id]
|
|
||||||
|
|
||||||
mov dword [sf_state.connected], 0
|
|
||||||
mov dword [sf_state.client_id], 0
|
|
||||||
|
|
||||||
DEBUGF 1, "[SF] Shutdown complete\n"
|
|
||||||
|
|
||||||
.not_connected:
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; ДАННЫЕ
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
; Page-aligned буфер для данных (16384 байт)
|
|
||||||
align 4096
|
|
||||||
sf_mappings_buffer: rb 16384
|
|
||||||
|
|
||||||
; PageList структура
|
|
||||||
align 16
|
|
||||||
sf_pagelist:
|
|
||||||
dd 1 ; cPages
|
|
||||||
sf_pagelist_pfn:
|
|
||||||
dd 0 ; pfn[0]
|
|
||||||
|
|
||||||
; Структуры пакетов (в постоянной памяти)
|
|
||||||
align 16
|
|
||||||
sf_query_mappings_pkt: rb 80 ; Пакет для QUERY_MAPPINGS
|
|
||||||
sf_query_map_name_pkt: rb 80 ; Пакет для QUERY_MAP_NAME
|
|
||||||
sf_map_folder_pkt: rb 80 ; Пакет для MAP_FOLDER
|
|
||||||
|
|
||||||
|
|
||||||
; SharedFolder packets (must be in permanent memory!)
|
|
||||||
align 4
|
|
||||||
; sf_query_mappings_pkt: rb sizeof.SHFL_QUERY_MAPPINGS
|
|
||||||
; sf_query_map_name_pkt: rb sizeof.SHFL_QUERY_MAP_NAME
|
|
||||||
; sf_map_folder_pkt: rb sizeof.SHFL_MAP_FOLDER
|
|
||||||
sf_create_pkt: rb sizeof.SHFL_CREATE
|
|
||||||
sf_read_pkt: rb sizeof.SHFL_READ
|
|
||||||
sf_write_pkt: rb sizeof.SHFL_WRITE
|
|
||||||
sf_close_pkt: rb sizeof.SHFL_CLOSE
|
|
||||||
sf_list_pkt: rb sizeof.SHFL_LIST
|
|
||||||
1119
services/shared_folders/shared_folders.inc
Normal file
1119
services/shared_folders/shared_folders.inc
Normal file
File diff suppressed because it is too large
Load Diff
3153
services/shared_folders/vboxsf.inc
Normal file
3153
services/shared_folders/vboxsf.inc
Normal file
File diff suppressed because it is too large
Load Diff
444
services/timesync/timesync.inc
Normal file
444
services/timesync/timesync.inc
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Time Sync Service
|
||||||
|
; Назначение : Синхронизация часов гостевой ОС с хостом VirtualBox
|
||||||
|
; Файл : services/timesync/timesync.inc
|
||||||
|
;
|
||||||
|
; VBox возвращает 64-bit миллисекунды с Unix epoch (1970-01-01).
|
||||||
|
; Драйвер пишет время напрямую в CMOS RTC (порты 0x70/0x71).
|
||||||
|
; int 0x40 нельзя из kernel-mode: обработчик syscall предполагает ring 3
|
||||||
|
; (переключение стеков user→kernel), из ring 0 это разрушает стек.
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
svc_timesync_name db "TIMESYNC", 0
|
||||||
|
|
||||||
|
; --- Data ---
|
||||||
|
align 4
|
||||||
|
vbox_timesync_enabled dd 0
|
||||||
|
vbox_timesync_last_tick dd 0
|
||||||
|
|
||||||
|
align 4
|
||||||
|
vmmdev_get_host_time_s VMMDEV_GET_HOST_TIME \
|
||||||
|
<sizeof.VMMDEV_GET_HOST_TIME, \
|
||||||
|
VMMDEV_REQUEST_HEADER_VERSION, \
|
||||||
|
VMMDEV_REQ_GET_HOST_TIME, \
|
||||||
|
0, 0, 0>, \
|
||||||
|
0, 0
|
||||||
|
|
||||||
|
align 4
|
||||||
|
timesync_time_virt dd 0
|
||||||
|
timesync_time_phys dd 0
|
||||||
|
|
||||||
|
; CMOS port constants
|
||||||
|
CMOS_ADDR equ 0x70
|
||||||
|
CMOS_DATA equ 0x71
|
||||||
|
|
||||||
|
; timesync_init — Инициализация
|
||||||
|
proc timesync_init
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Initializing...\n"
|
||||||
|
|
||||||
|
mov eax, vmmdev_get_host_time_s
|
||||||
|
mov [timesync_time_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [timesync_time_phys], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Buffer: virt=0x%x, phys=0x%x\n", \
|
||||||
|
[timesync_time_virt], [timesync_time_phys]
|
||||||
|
|
||||||
|
mov dword [vbox_timesync_enabled], 1
|
||||||
|
call timesync_do_sync
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Initialized OK\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; timesync_tick — Проверка интервала и синхронизация
|
||||||
|
proc timesync_tick
|
||||||
|
cmp dword [vbox_timesync_enabled], 0
|
||||||
|
je .done
|
||||||
|
invoke GetTimerTicks
|
||||||
|
sub eax, [vbox_timesync_last_tick]
|
||||||
|
cmp eax, TIMESYNC_INTERVAL_TICKS
|
||||||
|
jb .done
|
||||||
|
invoke GetTimerTicks
|
||||||
|
mov [vbox_timesync_last_tick], eax
|
||||||
|
call timesync_do_sync
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; timesync_do_sync — Получить время хоста и записать в CMOS RTC
|
||||||
|
proc timesync_do_sync uses ebx ecx edx esi edi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Syncing...\n"
|
||||||
|
|
||||||
|
mov edi, [timesync_time_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad_ptr
|
||||||
|
|
||||||
|
mov dword [edi + VMMDEV_GET_HOST_TIME.header.rc], 0
|
||||||
|
mov dword [edi + VMMDEV_GET_HOST_TIME.time_low], 0
|
||||||
|
mov dword [edi + VMMDEV_GET_HOST_TIME.time_high], 0
|
||||||
|
|
||||||
|
stdcall vmmdev_send_request, [timesync_time_phys]
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_GET_HOST_TIME.header.rc]
|
||||||
|
test eax, eax
|
||||||
|
js .error
|
||||||
|
|
||||||
|
; 64-bit миллисекунды с epoch
|
||||||
|
mov eax, [edi + VMMDEV_GET_HOST_TIME.time_low]
|
||||||
|
mov edx, [edi + VMMDEV_GET_HOST_TIME.time_high]
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Host ms: hi=0x%x lo=0x%x\n", edx, eax
|
||||||
|
|
||||||
|
mov ecx, eax
|
||||||
|
or ecx, edx
|
||||||
|
jz .bad_time
|
||||||
|
|
||||||
|
; edx:eax / 1000 → eax = unix seconds
|
||||||
|
mov ecx, 1000
|
||||||
|
div ecx
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Unix timestamp: %d\n", eax
|
||||||
|
call timesync_set_cmos
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Sync complete\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad_ptr:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Bad pointer\n"
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] GetHostTime failed: rc=0x%x\n", eax
|
||||||
|
ret
|
||||||
|
.bad_time:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Zero time returned\n"
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; timesync_set_cmos — Вычисляет дату/время и записывает в CMOS RTC
|
||||||
|
proc timesync_set_cmos uses ebx ecx edx esi edi
|
||||||
|
locals
|
||||||
|
ts_sec dd ?
|
||||||
|
ts_min dd ?
|
||||||
|
ts_hour dd ?
|
||||||
|
ts_day dd ?
|
||||||
|
ts_month dd ?
|
||||||
|
ts_year dd ?
|
||||||
|
ts_dow dd ?
|
||||||
|
endl
|
||||||
|
|
||||||
|
; ── Разложить на дни и время дня ──
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 86400
|
||||||
|
div ecx
|
||||||
|
; eax = total_days, edx = time_of_day_seconds
|
||||||
|
mov esi, eax ; total_days
|
||||||
|
|
||||||
|
; Время суток
|
||||||
|
mov eax, edx
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 3600
|
||||||
|
div ecx
|
||||||
|
mov [ts_hour], eax
|
||||||
|
|
||||||
|
mov eax, edx
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 60
|
||||||
|
div ecx
|
||||||
|
mov [ts_min], eax
|
||||||
|
mov [ts_sec], edx
|
||||||
|
|
||||||
|
; ── День недели: (days + 3) % 7 + 1 ──
|
||||||
|
lea eax, [esi + 3]
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 7
|
||||||
|
div ecx
|
||||||
|
inc edx
|
||||||
|
mov [ts_dow], edx
|
||||||
|
|
||||||
|
; ── civil_from_days (Howard Hinnant) ──
|
||||||
|
; eax = days → дата
|
||||||
|
mov eax, esi
|
||||||
|
call .civil_from_days
|
||||||
|
mov [ts_day], eax
|
||||||
|
mov [ts_month], ebx
|
||||||
|
mov [ts_year], ecx
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Date: %d-%d-%d %d:%d:%d DoW=%d\n", \
|
||||||
|
[ts_year], [ts_month], [ts_day], [ts_hour], [ts_min], [ts_sec], [ts_dow]
|
||||||
|
|
||||||
|
; ── Ждать окончания обновления RTC ──
|
||||||
|
call .cmos_wait
|
||||||
|
|
||||||
|
; ── Запретить обновления (SET bit в Status B) ──
|
||||||
|
mov al, 0x0B
|
||||||
|
out CMOS_ADDR, al
|
||||||
|
in al, CMOS_DATA
|
||||||
|
or al, 0x80
|
||||||
|
push eax
|
||||||
|
mov al, 0x0B
|
||||||
|
out CMOS_ADDR, al
|
||||||
|
pop eax
|
||||||
|
out CMOS_DATA, al
|
||||||
|
|
||||||
|
; ── Записать все поля (BCD) ──
|
||||||
|
mov eax, [ts_sec]
|
||||||
|
call .to_bcd
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x00 ; seconds register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
mov eax, [ts_min]
|
||||||
|
call .to_bcd
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x02 ; minutes register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
mov eax, [ts_hour]
|
||||||
|
call .to_bcd
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x04 ; hours register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
mov eax, [ts_dow]
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x06 ; weekday register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
mov eax, [ts_day]
|
||||||
|
call .to_bcd
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x07 ; day register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
mov eax, [ts_month]
|
||||||
|
call .to_bcd
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x08 ; month register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
; YY = year mod 100
|
||||||
|
mov eax, [ts_year]
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 100
|
||||||
|
div ecx
|
||||||
|
push eax ; century
|
||||||
|
mov eax, edx
|
||||||
|
call .to_bcd
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x09 ; year register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
; Century
|
||||||
|
pop eax
|
||||||
|
call .to_bcd
|
||||||
|
mov ah, al
|
||||||
|
mov al, 0x32 ; century register
|
||||||
|
call .cmos_wr
|
||||||
|
|
||||||
|
; ── Разрешить обновления ──
|
||||||
|
mov al, 0x0B
|
||||||
|
out CMOS_ADDR, al
|
||||||
|
in al, CMOS_DATA
|
||||||
|
and al, 0x7F
|
||||||
|
push eax
|
||||||
|
mov al, 0x0B
|
||||||
|
out CMOS_ADDR, al
|
||||||
|
pop eax
|
||||||
|
out CMOS_DATA, al
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
; ── Helpers ──
|
||||||
|
|
||||||
|
.cmos_wr:
|
||||||
|
; al = register, ah = value
|
||||||
|
out CMOS_ADDR, al
|
||||||
|
mov al, ah
|
||||||
|
out CMOS_DATA, al
|
||||||
|
ret
|
||||||
|
|
||||||
|
.cmos_wait:
|
||||||
|
push eax ecx
|
||||||
|
mov ecx, 10000
|
||||||
|
@@:
|
||||||
|
mov al, 0x0A
|
||||||
|
out CMOS_ADDR, al
|
||||||
|
in al, CMOS_DATA
|
||||||
|
test al, 0x80
|
||||||
|
jz @f
|
||||||
|
pause
|
||||||
|
loop @b
|
||||||
|
@@:
|
||||||
|
pop ecx eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.to_bcd:
|
||||||
|
; eax (0-99) → eax BCD
|
||||||
|
push ecx edx
|
||||||
|
and eax, 0xFF
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 10
|
||||||
|
div ecx
|
||||||
|
shl eax, 4
|
||||||
|
or eax, edx
|
||||||
|
pop edx ecx
|
||||||
|
ret
|
||||||
|
|
||||||
|
; .civil_from_days — Howard Hinnant algorithm
|
||||||
|
.civil_from_days:
|
||||||
|
; z = days + 719468
|
||||||
|
add eax, 719468
|
||||||
|
|
||||||
|
; era = z / 146097
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 146097
|
||||||
|
div ecx
|
||||||
|
mov esi, eax ; era
|
||||||
|
; doe = z - era * 146097 (но z уже потерян → используем edx = remainder)
|
||||||
|
mov ecx, edx ; ecx = doe
|
||||||
|
|
||||||
|
; yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365
|
||||||
|
mov eax, ecx
|
||||||
|
xor edx, edx
|
||||||
|
mov edi, 1460
|
||||||
|
div edi
|
||||||
|
mov edi, eax ; doe/1460
|
||||||
|
|
||||||
|
mov eax, ecx
|
||||||
|
xor edx, edx
|
||||||
|
push ecx
|
||||||
|
mov ecx, 36524
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
sub edi, eax ; doe/1460 - doe/36524 (note: we subtract then add later)
|
||||||
|
; Actually: val = doe - doe/1460 + doe/36524 - doe/146096
|
||||||
|
; Let me redo properly:
|
||||||
|
|
||||||
|
; A = doe/1460
|
||||||
|
push ecx
|
||||||
|
mov eax, ecx
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 1460
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
push eax ; [esp] = A
|
||||||
|
|
||||||
|
; B = doe/36524
|
||||||
|
push ecx
|
||||||
|
mov eax, ecx
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 36524
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
push eax ; [esp]=B, [esp+4]=A
|
||||||
|
|
||||||
|
; C = doe/146096
|
||||||
|
push ecx
|
||||||
|
mov eax, ecx
|
||||||
|
xor edx, edx
|
||||||
|
mov ecx, 146096
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
; eax = C
|
||||||
|
|
||||||
|
pop ebx ; B
|
||||||
|
pop edi ; A
|
||||||
|
|
||||||
|
; val = doe - A + B - C
|
||||||
|
mov edx, ecx ; doe
|
||||||
|
sub edx, edi
|
||||||
|
add edx, ebx
|
||||||
|
sub edx, eax
|
||||||
|
|
||||||
|
; yoe = val / 365
|
||||||
|
mov eax, edx
|
||||||
|
xor edx, edx
|
||||||
|
push ecx
|
||||||
|
mov ecx, 365
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
; eax = yoe, ecx still = doe
|
||||||
|
mov edi, eax ; edi = yoe
|
||||||
|
|
||||||
|
; year = yoe + era * 400
|
||||||
|
mov eax, esi ; era
|
||||||
|
imul eax, 400
|
||||||
|
add eax, edi
|
||||||
|
push eax ; [esp] = year
|
||||||
|
|
||||||
|
; doy = doe - (365*yoe + yoe/4 - yoe/100)
|
||||||
|
mov eax, edi
|
||||||
|
imul eax, 365
|
||||||
|
mov esi, eax ; 365*yoe
|
||||||
|
|
||||||
|
mov eax, edi
|
||||||
|
shr eax, 2
|
||||||
|
add esi, eax ; + yoe/4
|
||||||
|
|
||||||
|
mov eax, edi
|
||||||
|
xor edx, edx
|
||||||
|
push ecx
|
||||||
|
mov ecx, 100
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
sub esi, eax ; - yoe/100
|
||||||
|
|
||||||
|
sub ecx, esi ; ecx = doy
|
||||||
|
|
||||||
|
; mp = (5*doy + 2) / 153
|
||||||
|
imul eax, ecx, 5
|
||||||
|
add eax, 2
|
||||||
|
xor edx, edx
|
||||||
|
push ecx
|
||||||
|
mov ecx, 153
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
mov edi, eax ; mp
|
||||||
|
|
||||||
|
; day = doy - (153*mp + 2)/5 + 1
|
||||||
|
imul eax, edi, 153
|
||||||
|
add eax, 2
|
||||||
|
xor edx, edx
|
||||||
|
push ecx
|
||||||
|
mov ecx, 5
|
||||||
|
div ecx
|
||||||
|
pop ecx
|
||||||
|
sub ecx, eax
|
||||||
|
inc ecx ; ecx = day
|
||||||
|
|
||||||
|
; month = mp < 10 ? mp + 3 : mp - 9
|
||||||
|
cmp edi, 10
|
||||||
|
jae .mp_ge10
|
||||||
|
add edi, 3
|
||||||
|
jmp .month_done2
|
||||||
|
.mp_ge10:
|
||||||
|
sub edi, 9
|
||||||
|
.month_done2:
|
||||||
|
; edi = month, ecx = day
|
||||||
|
|
||||||
|
; if month <= 2 → year++
|
||||||
|
pop eax ; year
|
||||||
|
cmp edi, 2
|
||||||
|
ja .year_ok2
|
||||||
|
inc eax
|
||||||
|
.year_ok2:
|
||||||
|
; Формируем результат: eax=day, ebx=month, ecx=year
|
||||||
|
mov ebx, edi ; month
|
||||||
|
xchg eax, ecx ; eax=day, ecx=year
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; timesync_disable — Выключить timesync
|
||||||
|
proc timesync_disable
|
||||||
|
DEBUGF 2, "[VBoxGuest] [TimeSync] Disabling...\n"
|
||||||
|
mov dword [vbox_timesync_enabled], 0
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
REGISTER_SERVICE svc_timesync_name, TIMESYNC_EVENT_MASK, 0, \
|
||||||
|
timesync_init, 0, timesync_disable, 0, timesync_tick, AUTOSTART_TIMESYNC
|
||||||
70
sys/init.inc
Normal file
70
sys/init.inc
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Инициализация драйвера VBoxGuest
|
||||||
|
; Назначение : Инициализация всех подсистем драйвера, подключение к VMMDev,
|
||||||
|
; настройка сервисов, установка обработчиков
|
||||||
|
; Файл : sys/init.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Полная инициализация драйвера VBoxGuest
|
||||||
|
proc sys_init
|
||||||
|
|
||||||
|
call vmmdev_probe
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call mmio_map_vmmdev
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call ports_init ; чекалка
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call vmmdev_init_packets
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call vmmdev_check_version ; получить версию хоста (для guest_info2)
|
||||||
|
|
||||||
|
call vmmdev_init_protocol ; guest_info + caps + event_filter
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call hgcm_init ; подготовка пакетов/таймаутов
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call timer_init
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call vmmdev_irq_install
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
call dispatcher_init_all
|
||||||
|
call dispatcher_enable_autostart
|
||||||
|
|
||||||
|
call dispatcher_get_active_events
|
||||||
|
call vmmdev_update_event_filter
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail_event_filter
|
||||||
|
|
||||||
|
call dispatcher_get_active_caps
|
||||||
|
call vmmdev_update_capabilities
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail_caps
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail_event_filter:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Init] Event filter setup failed: 0x%x\n", eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail_caps:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Init] Guest capabilities setup failed: 0x%x\n", eax
|
||||||
|
ret
|
||||||
|
.fail:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
183
sys/ioctl.inc
Normal file
183
sys/ioctl.inc
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : IOCTL интерфейс драйвера VBoxGuest
|
||||||
|
; Назначение : Обработка команд от приложений (service_proc, ioctl_get_services)
|
||||||
|
; Файл : sys/ioctl.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; IOCTL ABI v1
|
||||||
|
API_VERSION equ 1
|
||||||
|
|
||||||
|
; General
|
||||||
|
VBOX_IOCTL_GET_VERSION equ 0
|
||||||
|
VBOX_IOCTL_GET_SERVICES equ 1
|
||||||
|
VBOX_IOCTL_SVC_ENABLE equ 2
|
||||||
|
VBOX_IOCTL_SVC_DISABLE equ 3
|
||||||
|
|
||||||
|
; Clipboard
|
||||||
|
VBOX_IOCTL_CLIP_STATUS equ 10
|
||||||
|
VBOX_IOCTL_CLIP_READ equ 11
|
||||||
|
VBOX_IOCTL_CLIP_WRITE equ 12
|
||||||
|
|
||||||
|
; SERVICE_INFO size for GET_SERVICES (dd id + rb name[16] + dd enabled = 24)
|
||||||
|
SVC_INFO_SIZE equ 24
|
||||||
|
SVC_INFO_NAME_LEN equ 16
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Service Procedure — IOCTL dispatch
|
||||||
|
; =============================================================================
|
||||||
|
proc service_proc stdcall, ioctl:dword
|
||||||
|
mov ebx, [ioctl]
|
||||||
|
mov eax, [ebx + IOCTL.io_code]
|
||||||
|
|
||||||
|
cmp eax, VBOX_IOCTL_GET_VERSION
|
||||||
|
je .get_version
|
||||||
|
cmp eax, VBOX_IOCTL_GET_SERVICES
|
||||||
|
je .get_services
|
||||||
|
cmp eax, VBOX_IOCTL_SVC_ENABLE
|
||||||
|
je .svc_enable
|
||||||
|
cmp eax, VBOX_IOCTL_SVC_DISABLE
|
||||||
|
je .svc_disable
|
||||||
|
cmp eax, VBOX_IOCTL_CLIP_STATUS
|
||||||
|
je .clip_status
|
||||||
|
cmp eax, VBOX_IOCTL_CLIP_READ
|
||||||
|
je .clip_read
|
||||||
|
cmp eax, VBOX_IOCTL_CLIP_WRITE
|
||||||
|
je .clip_write
|
||||||
|
|
||||||
|
jmp .fail
|
||||||
|
|
||||||
|
; --- GET_VERSION (0) ---
|
||||||
|
.get_version:
|
||||||
|
cmp [ebx + IOCTL.out_size], 4
|
||||||
|
jb .fail
|
||||||
|
mov eax, [ebx + IOCTL.output]
|
||||||
|
mov dword [eax], API_VERSION
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
; --- GET_SERVICES (1) ---
|
||||||
|
.get_services:
|
||||||
|
stdcall ioctl_get_services, ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
; --- SVC_ENABLE (2) ---
|
||||||
|
.svc_enable:
|
||||||
|
cmp [ebx + IOCTL.inp_size], 4
|
||||||
|
jb .fail
|
||||||
|
mov eax, [ebx + IOCTL.input]
|
||||||
|
mov eax, [eax]
|
||||||
|
stdcall dispatcher_enable_by_id, eax
|
||||||
|
push eax
|
||||||
|
call dispatcher_get_active_events
|
||||||
|
call vmmdev_update_event_filter
|
||||||
|
call dispatcher_get_active_caps
|
||||||
|
call vmmdev_update_capabilities
|
||||||
|
pop eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
; --- SVC_DISABLE (3) ---
|
||||||
|
.svc_disable:
|
||||||
|
cmp [ebx + IOCTL.inp_size], 4
|
||||||
|
jb .fail
|
||||||
|
mov eax, [ebx + IOCTL.input]
|
||||||
|
mov eax, [eax]
|
||||||
|
stdcall dispatcher_disable_by_id, eax
|
||||||
|
push eax
|
||||||
|
call dispatcher_get_active_events
|
||||||
|
call vmmdev_update_event_filter
|
||||||
|
call dispatcher_get_active_caps
|
||||||
|
call vmmdev_update_capabilities
|
||||||
|
pop eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
; --- CLIP_STATUS (10) ---
|
||||||
|
.clip_status:
|
||||||
|
stdcall clip_ioctl_status, ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
; --- CLIP_READ (11) ---
|
||||||
|
.clip_read:
|
||||||
|
stdcall clip_ioctl_read, ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
; --- CLIP_WRITE (12) ---
|
||||||
|
.clip_write:
|
||||||
|
stdcall clip_ioctl_write, ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail:
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; ioctl_get_services — Заполнить список сервисов
|
||||||
|
; =============================================================================
|
||||||
|
proc ioctl_get_services stdcall uses ebx ecx edx esi edi, ioctl_ptr:dword
|
||||||
|
mov ebx, [ioctl_ptr]
|
||||||
|
cmp [ebx + IOCTL.out_size], 4
|
||||||
|
jb .fail
|
||||||
|
|
||||||
|
mov edi, [ebx + IOCTL.output]
|
||||||
|
mov ecx, [services_count]
|
||||||
|
mov [edi], ecx ; dd count
|
||||||
|
add edi, 4
|
||||||
|
|
||||||
|
test ecx, ecx
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
; Доступное место = out_size - 4
|
||||||
|
mov edx, [ebx + IOCTL.out_size]
|
||||||
|
sub edx, 4
|
||||||
|
|
||||||
|
mov esi, services_table
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
cmp edx, SVC_INFO_SIZE
|
||||||
|
jb .done
|
||||||
|
|
||||||
|
; dd id
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.id]
|
||||||
|
mov [edi], eax
|
||||||
|
add edi, 4
|
||||||
|
|
||||||
|
; rb name[16] — null-padded
|
||||||
|
push ecx esi edi
|
||||||
|
mov ecx, SVC_INFO_NAME_LEN
|
||||||
|
; Заполнить нулями
|
||||||
|
push edi
|
||||||
|
xor al, al
|
||||||
|
rep stosb
|
||||||
|
pop edi
|
||||||
|
; Скопировать имя
|
||||||
|
mov esi, [esi + SERVICE_ENTRY.name_ptr]
|
||||||
|
mov ecx, SVC_INFO_NAME_LEN - 1
|
||||||
|
.copy_name:
|
||||||
|
lodsb
|
||||||
|
test al, al
|
||||||
|
jz .name_done
|
||||||
|
stosb
|
||||||
|
dec ecx
|
||||||
|
jnz .copy_name
|
||||||
|
.name_done:
|
||||||
|
pop edi esi ecx
|
||||||
|
add edi, SVC_INFO_NAME_LEN
|
||||||
|
|
||||||
|
; dd enabled
|
||||||
|
mov eax, [esi + SERVICE_ENTRY.enabled]
|
||||||
|
mov [edi], eax
|
||||||
|
add edi, 4
|
||||||
|
|
||||||
|
sub edx, SVC_INFO_SIZE
|
||||||
|
add esi, sizeof.SERVICE_ENTRY
|
||||||
|
dec ecx
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
.done:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail:
|
||||||
|
or eax, -1
|
||||||
|
ret
|
||||||
|
endp
|
||||||
47
sys/shutdown.inc
Normal file
47
sys/shutdown.inc
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : Корректное завершение работы драйвера VBoxGuest
|
||||||
|
; Назначение : Деинициализация всех подсистем при выгрузке драйвера (DRV_EXIT)
|
||||||
|
; Файл : sys/shutdown.inc
|
||||||
|
; Порядок : По образцу Linux vbg_core_exit для избежания зависаний
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Полная деинициализация драйвера VBoxGuest (вызывается при DRV_EXIT)
|
||||||
|
proc sys_shutdown
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] Shutdown started\n"
|
||||||
|
|
||||||
|
; 1. Остановить все сервисы (отправить уведомления хосту)
|
||||||
|
call dispatcher_disable_all
|
||||||
|
DEBUGF 2, "[VBoxGuest] All services disabled\n"
|
||||||
|
|
||||||
|
; 2. Остановить таймер (ПЕРЕД IRQ detach, иначе tick вызовется во время cleanup)
|
||||||
|
call timer_stop
|
||||||
|
DEBUGF 2, "[VBoxGuest] Timer stopped\n"
|
||||||
|
|
||||||
|
; 3. Убрать IRQ handler ОБЯЗАТЕЛЬНО до освобождения буферов
|
||||||
|
; ISR не должна писать в освобождаемую память
|
||||||
|
; call vbox_irq_detach
|
||||||
|
; DEBUGF 2, "[VBoxGuest] IRQ handler detached\n"
|
||||||
|
|
||||||
|
; 4. Сбросить маски события и возможностей на хосте (маска = 0)
|
||||||
|
xor eax, eax
|
||||||
|
mov [dispatcher_active_events], eax
|
||||||
|
call vmmdev_update_event_filter
|
||||||
|
DEBUGF 2, "[VBoxGuest] Event filter cleared\n"
|
||||||
|
|
||||||
|
mov [dispatcher_active_caps], eax
|
||||||
|
call vmmdev_update_capabilities
|
||||||
|
DEBUGF 2, "[VBoxGuest] Capabilities cleared\n"
|
||||||
|
|
||||||
|
; 5. Освободить пакеты (ПОСЛЕ IRQ detach — ISR больше не пишет в них)
|
||||||
|
call hgcm_free_packets
|
||||||
|
DEBUGF 2, "[VBoxGuest] HGCM packets freed\n"
|
||||||
|
|
||||||
|
call vmmdev_free_packets
|
||||||
|
DEBUGF 2, "[VBoxGuest] VMMDev packets freed\n"
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] Shutdown complete\n"
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
endp
|
||||||
BIN
vboxctrl/vboxctrl
Normal file
BIN
vboxctrl/vboxctrl
Normal file
Binary file not shown.
1102
vboxctrl/vboxctrl.asm
Normal file
1102
vboxctrl/vboxctrl.asm
Normal file
File diff suppressed because it is too large
Load Diff
176
vboxguest.asm
176
vboxguest.asm
@@ -1,26 +1,19 @@
|
|||||||
; =============================================================================
|
; =============================================================================
|
||||||
; Программа : VirtualBox Guest Driver для KolibriOS
|
; Программа : VirtualBox Guest Driver для KolibriOS
|
||||||
; Модуль : Main driver entry
|
; Модуль : Main driver entry
|
||||||
; Назначение : Full-featured driver with Clipboard support and dual-level error handling
|
; Назначение : Full-featured driver
|
||||||
; Файл : vboxguest.asm
|
; Файл : vboxguest.asm
|
||||||
|
; Автор : lex_coder(lex_coder@mail.ru), Алексей Михайлов
|
||||||
;
|
;
|
||||||
; cp /usbhd0/1/vboxguest/vboxguest /sys/Drivers/vboxguest.sys
|
; cp /usbhd0/1/vboxguest/vboxguest /sys/Drivers/vboxguest.sys
|
||||||
; loaddrv vboxguest
|
; loaddrv vboxguest
|
||||||
;
|
;
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
|
|
||||||
format PE DLL native 0.05
|
format PE DLL native 0.05
|
||||||
entry START
|
entry START
|
||||||
|
|
||||||
__DEBUG__ = 1
|
|
||||||
__DEBUG_LEVEL__ = 1
|
|
||||||
|
|
||||||
API_VERSION equ 0
|
|
||||||
SRV_GETVERSION equ 0
|
|
||||||
|
|
||||||
section '.flat' code readable writable executable
|
section '.flat' code readable writable executable
|
||||||
|
|
||||||
; KolibriOS includes
|
|
||||||
include '/usbhd0/1/ksrc_n/DRIVERS/proc32.inc'
|
include '/usbhd0/1/ksrc_n/DRIVERS/proc32.inc'
|
||||||
include '/usbhd0/1/ksrc_n/DRIVERS/struct.inc'
|
include '/usbhd0/1/ksrc_n/DRIVERS/struct.inc'
|
||||||
include '/usbhd0/1/ksrc_n/DRIVERS/macros.inc'
|
include '/usbhd0/1/ksrc_n/DRIVERS/macros.inc'
|
||||||
@@ -28,169 +21,59 @@ include '/usbhd0/1/ksrc_n/DRIVERS/peimport.inc'
|
|||||||
include '/usbhd0/1/ksrc_n/DRIVERS/fdo.inc'
|
include '/usbhd0/1/ksrc_n/DRIVERS/fdo.inc'
|
||||||
include '/usbhd0/1/ksrc_n/DRIVERS/pci.inc'
|
include '/usbhd0/1/ksrc_n/DRIVERS/pci.inc'
|
||||||
|
|
||||||
; VBoxGuest driver modules
|
include 'config.inc'
|
||||||
include 'common/log.inc'
|
include 'common/common.inc'
|
||||||
include 'common/constants.inc'
|
include 'data/data.inc'
|
||||||
include 'common/structs.inc'
|
include 'core/core.inc'
|
||||||
include 'common/macros.inc'
|
include 'vmmdev/vmmdev.inc'
|
||||||
include 'common/errors.inc'
|
include 'hgcm/core.inc'
|
||||||
include 'core/vmmdev.inc'
|
include 'sys/init.inc'
|
||||||
include 'core/hgcm.inc'
|
include 'sys/shutdown.inc'
|
||||||
include 'core/irq.inc'
|
include 'sys/ioctl.inc'
|
||||||
include 'services/display.inc'
|
include 'services/services.inc'
|
||||||
include 'services/clipboard.inc'
|
|
||||||
include 'services/shared_folder.inc'
|
BUILD_SERVICE_TABLE
|
||||||
|
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
; Driver Entry Point
|
; Driver Entry Point
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
|
|
||||||
proc START c, state:dword, cmdline:dword
|
proc START c, state:dword, cmdline:dword
|
||||||
push ebx esi edi
|
push ebx esi edi
|
||||||
cmp [state], DRV_ENTRY
|
cmp [state], DRV_ENTRY
|
||||||
jne .exit
|
je .entry
|
||||||
|
cmp [state], DRV_EXIT
|
||||||
|
je .exit
|
||||||
|
|
||||||
|
pop edi esi ebx
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
.entry:
|
.entry:
|
||||||
DEBUGF 1, "[VBoxGuest] ========================================\n"
|
DEBUGF 2, "[VBoxGuest] VBoxGuest Driver v2.0\n"
|
||||||
DEBUGF 1, "[VBoxGuest] VBoxGuest Driver v1.3\n"
|
|
||||||
DEBUGF 1, "[VBoxGuest] Clipboard + Display + Dual-level Errors\n"
|
|
||||||
DEBUGF 1, "[VBoxGuest] ========================================\n"
|
|
||||||
|
|
||||||
; 1. Find PCI device
|
call sys_init ;
|
||||||
stdcall detect
|
|
||||||
test eax, eax
|
|
||||||
jz .fail
|
|
||||||
|
|
||||||
; 2. Initialize device (BAR, IRQ, MMIO)
|
|
||||||
stdcall vmmdev_init, ebx
|
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jnz .fail
|
jnz .fail
|
||||||
|
|
||||||
stdcall vbox_service_init
|
; Регистрация сервиса (IOCTL интерфейс для приложений)
|
||||||
|
|
||||||
; 3. Send guest info
|
|
||||||
call vmmdev_send_guest_info
|
|
||||||
test eax, eax
|
|
||||||
jnz .fail
|
|
||||||
|
|
||||||
; 4. Set capabilities
|
|
||||||
call vmmdev_set_guest_caps
|
|
||||||
test eax, eax
|
|
||||||
jnz .fail
|
|
||||||
|
|
||||||
; 7. Enable interrupts
|
|
||||||
call vmmdev_enable_interrupts
|
|
||||||
|
|
||||||
|
|
||||||
; 5. Initialize prepared packets
|
|
||||||
call init_packets
|
|
||||||
|
|
||||||
; 6. Initialize clipboard
|
|
||||||
call clipboard_init
|
|
||||||
test eax, eax
|
|
||||||
jnz .clipboard_failed
|
|
||||||
DEBUGF 2, "[VBoxGuest] Clipboard initialized successfully\n"
|
|
||||||
jmp .clipboard_ok
|
|
||||||
.clipboard_failed:
|
|
||||||
DEBUGF 1, "[VBoxGuest] Clipboard initialization failed (non-fatal)\n"
|
|
||||||
.clipboard_ok:
|
|
||||||
|
|
||||||
call sf_init
|
|
||||||
|
|
||||||
; 8. Set initial display
|
|
||||||
call display_change
|
|
||||||
|
|
||||||
; 9. Register service
|
|
||||||
invoke RegService, service_name, service_proc
|
invoke RegService, service_name, service_proc
|
||||||
|
|
||||||
DEBUGF 1, "[VBoxGuest] ========================================\n"
|
DEBUGF 2, "[VBoxGuest] IOCTL service '%s' registered (API v%d)\n", service_name, API_VERSION
|
||||||
DEBUGF 1, "[VBoxGuest] Initialization complete!\n"
|
DEBUGF 2, "[VBoxGuest] Initialization complete!\n"
|
||||||
DEBUGF 1, "[VBoxGuest] ========================================\n"
|
|
||||||
|
|
||||||
pop edi esi ebx
|
pop edi esi ebx
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.fail:
|
.fail:
|
||||||
DEBUGF 1, "[VBoxGuest] Initialization FAILED\n"
|
DEBUGF 2, "[VBoxGuest] Initialization FAILED\n"
|
||||||
|
|
||||||
.exit:
|
.exit:
|
||||||
|
DEBUGF 2, "[VBoxGuest] DRV_EXIT: Shutting down driver\n"
|
||||||
|
call sys_shutdown
|
||||||
pop edi esi ebx
|
pop edi esi ebx
|
||||||
xor eax, eax
|
xor eax, eax
|
||||||
ret
|
ret
|
||||||
endp
|
endp
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Service Procedure
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
proc service_proc stdcall, ioctl:dword
|
|
||||||
mov ebx, [ioctl]
|
|
||||||
mov eax, [ebx + IOCTL.io_code]
|
|
||||||
cmp eax, SRV_GETVERSION
|
|
||||||
jne .fail
|
|
||||||
|
|
||||||
mov eax, [ebx + IOCTL.output]
|
|
||||||
cmp [ebx + IOCTL.out_size], 4
|
|
||||||
jne .fail
|
|
||||||
|
|
||||||
mov dword [eax], API_VERSION
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
.fail:
|
|
||||||
or eax, -1
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Detect VirtualBox PCI Device
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
proc detect stdcall
|
|
||||||
invoke GetPCIList
|
|
||||||
mov ebx, eax
|
|
||||||
|
|
||||||
.next_dev:
|
|
||||||
mov eax, [eax + PCIDEV.fd]
|
|
||||||
cmp eax, ebx
|
|
||||||
jz .not_found
|
|
||||||
|
|
||||||
mov edx, [eax + PCIDEV.vendor_device_id]
|
|
||||||
cmp edx, (VBOX_DEVICE_ID shl 16) + VBOX_VENDOR_ID
|
|
||||||
jne .next_dev
|
|
||||||
|
|
||||||
.found:
|
|
||||||
DEBUGF 2, "[VBoxGuest] VirtualBox device found\n"
|
|
||||||
mov ebx, eax
|
|
||||||
mov eax, 1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.not_found:
|
|
||||||
DEBUGF 1, "[VBoxGuest] VirtualBox device NOT found\n"
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; Initialize Prepared Packets
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
proc init_packets
|
|
||||||
; ACK events packet
|
|
||||||
mov eax, vmmdev_ack
|
|
||||||
mov [vbox_device.ack_virt], eax
|
|
||||||
invoke GetPhysAddr
|
|
||||||
mov [vbox_device.ack_phys], eax
|
|
||||||
|
|
||||||
; Display change packet
|
|
||||||
mov eax, vmmdev_display
|
|
||||||
mov [vbox_device.display_virt], eax
|
|
||||||
invoke GetPhysAddr
|
|
||||||
mov [vbox_device.display_phys], eax
|
|
||||||
|
|
||||||
DEBUGF 2, "[VBoxGuest] Packets initialized\n"
|
|
||||||
ret
|
|
||||||
endp
|
|
||||||
|
|
||||||
; =============================================================================
|
; =============================================================================
|
||||||
; Data Section
|
; Data Section
|
||||||
@@ -199,7 +82,6 @@ align 4
|
|||||||
service_name db 'VBOXGUEST', 0
|
service_name db 'VBOXGUEST', 0
|
||||||
|
|
||||||
align 4
|
align 4
|
||||||
; VBox device structure
|
|
||||||
vbox_device VBOX_DEVICE
|
vbox_device VBOX_DEVICE
|
||||||
|
|
||||||
include_debug_strings
|
include_debug_strings
|
||||||
|
|||||||
27
vmmdev/capabilities.inc
Normal file
27
vmmdev/capabilities.inc
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Guest Capabilities
|
||||||
|
; Назначение : Настройка возможностей гостя (REQ 56)
|
||||||
|
; Файл : vmmdev/capabilities.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; vmmdev_update_capabilities — Обновить caps по runtime-маске из dispatcher
|
||||||
|
proc vmmdev_update_capabilities
|
||||||
|
mov edi, [vbox_device.caps_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad
|
||||||
|
|
||||||
|
; Обновить or_mask в пакете
|
||||||
|
mov [edi + VMMDEV_SET_GUEST_CAPABILITIES2.or_mask], eax
|
||||||
|
mov dword [edi + VMMDEV_SET_GUEST_CAPABILITIES2.header.rc], 0
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Caps] Updating capabilities: or_mask=0x%x\n", eax
|
||||||
|
|
||||||
|
mov ebx, [vbox_device.caps_phys]
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_SET_GUEST_CAPABILITIES2.header.rc]
|
||||||
|
ret
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
25
vmmdev/core.inc
Normal file
25
vmmdev/core.inc
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Core
|
||||||
|
; Назначение : Базовые операции VMMDev: send_request, init протокола
|
||||||
|
; Файл : vmmdev/core.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Базовая настройка протокола VMMDev
|
||||||
|
proc vmmdev_init_protocol
|
||||||
|
|
||||||
|
; GuestInfo
|
||||||
|
call guest_info_report
|
||||||
|
test eax, eax
|
||||||
|
jnz .fail
|
||||||
|
|
||||||
|
; GuestInfo2 (non-fatal — не все версии VBox поддерживают)
|
||||||
|
call guest_info_2_report
|
||||||
|
test eax, eax
|
||||||
|
jz @f
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] GuestInfo2 failed (rc=0x%x), continuing...\n", eax
|
||||||
|
@@:
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
.fail:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
28
vmmdev/event_filter.inc
Normal file
28
vmmdev/event_filter.inc
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Event Filter
|
||||||
|
; Назначение : Фильтр событий VMMDev (REQ 42)
|
||||||
|
; Файл : vmmdev/event_filter.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; vmmdev_update_event_filter — Обновить фильтр по маске из dispatcher
|
||||||
|
proc vmmdev_update_event_filter
|
||||||
|
mov edi, [vbox_device.filter_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad
|
||||||
|
|
||||||
|
; Обновить or_mask в пакете
|
||||||
|
mov [edi + VMMDEV_CTL_GUEST_FILTER_MASK.or_mask], eax
|
||||||
|
mov dword [edi + VMMDEV_CTL_GUEST_FILTER_MASK.header.rc], 0
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [Filter] Updating event filter: or_mask=0x%x\n", eax
|
||||||
|
|
||||||
|
mov ebx, [vbox_device.filter_phys]
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
|
||||||
|
mov eax, [edi + VMMDEV_CTL_GUEST_FILTER_MASK.header.rc]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
77
vmmdev/guest_info.inc
Normal file
77
vmmdev/guest_info.inc
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Guest Info
|
||||||
|
; Назначение : ReportGuestInfo / ReportGuestInfo2
|
||||||
|
; Файл : vmmdev/guest_info.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Отправка ReportGuestInfo (REQ 50)
|
||||||
|
proc guest_info_report uses ebx esi
|
||||||
|
mov esi, [vbox_device.guestinfo_virt]
|
||||||
|
mov ebx, [vbox_device.guestinfo_phys]
|
||||||
|
test esi, esi
|
||||||
|
jz .bad
|
||||||
|
test ebx, ebx
|
||||||
|
jz .bad
|
||||||
|
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
mov eax, [esi + VMMDEV_HEADER.rc]
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Guest info sent (interface 0x%x)\n", VMMDEV_VERSION
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] send_guest_info failed, rc=0x%x\n", eax
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_PARAMETER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
; Отправка ReportGuestInfo2 (REQ 58), версия заполняется из host_version
|
||||||
|
proc guest_info_2_report uses ebx esi edi
|
||||||
|
mov esi, [vbox_device.guestinfo2_virt]
|
||||||
|
mov ebx, [vbox_device.guestinfo2_phys]
|
||||||
|
test esi, esi
|
||||||
|
jz .bad
|
||||||
|
test ebx, ebx
|
||||||
|
jz .bad
|
||||||
|
|
||||||
|
; Заполнить версию GA из полученной версии хоста
|
||||||
|
mov edi, [vbox_device.host_version_virt]
|
||||||
|
test edi, edi
|
||||||
|
jz .send ; нет данных — отправляем как есть (нули)
|
||||||
|
|
||||||
|
movzx eax, word [edi + VMMDEV_GET_HOST_VERSION.major]
|
||||||
|
mov word [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_major], ax
|
||||||
|
movzx eax, word [edi + VMMDEV_GET_HOST_VERSION.minor]
|
||||||
|
mov word [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_minor], ax
|
||||||
|
mov eax, [edi + VMMDEV_GET_HOST_VERSION.build]
|
||||||
|
mov dword [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_build], eax
|
||||||
|
mov eax, [edi + VMMDEV_GET_HOST_VERSION.revision]
|
||||||
|
mov dword [esi + VMMDEV_REPORT_GUEST_INFO2.guest_info.additions_revision], eax
|
||||||
|
|
||||||
|
.send:
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
mov eax, [esi + VMMDEV_HEADER.rc]
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Guest info 2 sent (interface 0x%x)\n", VMMDEV_VERSION
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] send_guest_info_2 failed, rc=0x%x\n", eax
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_PARAMETER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
51
vmmdev/hypervisor.inc
Normal file
51
vmmdev/hypervisor.inc
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Host/Hypervisor Info
|
||||||
|
; Назначение : Получение информации о хосте (REQ 4: GetHostVersion)
|
||||||
|
; Файл : vmmdev/hypervisor.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; Отправить REQ 4 GetHostVersion, eax = header.rc
|
||||||
|
proc vmmdev_get_host_version uses ebx edi
|
||||||
|
mov edi, [vbox_device.host_version_virt]
|
||||||
|
mov ebx, [vbox_device.host_version_phys]
|
||||||
|
test edi, edi
|
||||||
|
jz .bad
|
||||||
|
test ebx, ebx
|
||||||
|
jz .bad
|
||||||
|
|
||||||
|
stdcall vmmdev_send_request, ebx
|
||||||
|
mov eax, [edi + VMMDEV_HEADER.rc]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.bad:
|
||||||
|
mov eax, VERR_INVALID_POINTER
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
proc vmmdev_check_version uses ebx
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Checking host version (GetHostVersion)...\n"
|
||||||
|
|
||||||
|
stdcall vmmdev_get_host_version
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] GetHostVersion rc=0x%x\n", eax
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
mov eax, [vbox_device.host_version_virt]
|
||||||
|
movzx ebx, [eax + VMMDEV_GET_HOST_VERSION.major]
|
||||||
|
movzx ecx, [eax + VMMDEV_GET_HOST_VERSION.minor]
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Host version: %d.%d.%d r%d features=0x%x\n", \
|
||||||
|
ebx, ecx, \
|
||||||
|
[eax + VMMDEV_GET_HOST_VERSION.build], \
|
||||||
|
[eax + VMMDEV_GET_HOST_VERSION.revision], \
|
||||||
|
[eax + VMMDEV_GET_HOST_VERSION.features]
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] ERROR: GetHostVersion failed, rc=0x%x\n", eax
|
||||||
|
mov eax, VERR_GENERAL_FAILURE
|
||||||
|
ret
|
||||||
|
endp
|
||||||
169
vmmdev/packets.inc
Normal file
169
vmmdev/packets.inc
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Packets
|
||||||
|
; Назначение : Выделение и инициализация пакетов VMMDev (одна страница 4KB)
|
||||||
|
; Файл : vmmdev/packets.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
VMMDEV_PKT_OFS_HOST_VERSION = 0 ; sizeof.VMMDEV_GET_HOST_VERSION
|
||||||
|
VMMDEV_PKT_OFS_GUEST_INFO = 64 ; sizeof.VMMDEV_REPORT_GUEST_INFO
|
||||||
|
VMMDEV_PKT_OFS_GUEST_INFO2 = 128 ; sizeof.VMMDEV_REPORT_GUEST_INFO2 (~168)
|
||||||
|
VMMDEV_PKT_OFS_CAPS = 320 ; sizeof.VMMDEV_SET_GUEST_CAPABILITIES2
|
||||||
|
VMMDEV_PKT_OFS_FILTER = 384 ; sizeof.VMMDEV_CTL_GUEST_FILTER_MASK
|
||||||
|
VMMDEV_PKT_OFS_ACK_EVENTS = 448 ; sizeof.VMMDEV_ACKNOWLEDGE_EVENTS
|
||||||
|
|
||||||
|
; Строка версии (read-only, для копирования в пакет)
|
||||||
|
vmmdev_version_string db '1.0.0_KolibriOS', 0
|
||||||
|
VMMDEV_VERSION_STRING_LEN = $ - vmmdev_version_string
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; vmmdev_init_packets — Выделить страницу и инициализировать все VMMDev пакеты
|
||||||
|
; =============================================================================
|
||||||
|
proc vmmdev_init_packets uses ebx ecx edx esi edi
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Init packets (dynamic)\n"
|
||||||
|
|
||||||
|
; --- Выделить страницу ---
|
||||||
|
invoke KernelAlloc, 4096
|
||||||
|
test eax, eax
|
||||||
|
jz .alloc_fail
|
||||||
|
|
||||||
|
mov [vbox_device.vmmdev_packets_page], eax
|
||||||
|
|
||||||
|
; Обнулить страницу
|
||||||
|
mov edi, eax
|
||||||
|
xor eax, eax
|
||||||
|
mov ecx, 4096 / 4
|
||||||
|
rep stosd
|
||||||
|
|
||||||
|
mov ebx, [vbox_device.vmmdev_packets_page]
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 1) HOST_VERSION (offset 0)
|
||||||
|
; =================================================================
|
||||||
|
lea edi, [ebx + VMMDEV_PKT_OFS_HOST_VERSION]
|
||||||
|
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_GET_HOST_VERSION
|
||||||
|
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_GET_HOST_VERSION
|
||||||
|
|
||||||
|
mov [vbox_device.host_version_virt], edi
|
||||||
|
mov eax, edi
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.host_version_phys], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] HostVersion packet: virt=0x%x, phys=0x%x\n", \
|
||||||
|
[vbox_device.host_version_virt], eax
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 2) REPORT_GUEST_INFO (offset 64)
|
||||||
|
; =================================================================
|
||||||
|
lea edi, [ebx + VMMDEV_PKT_OFS_GUEST_INFO]
|
||||||
|
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_REPORT_GUEST_INFO
|
||||||
|
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_REPORT_GUEST_INFO
|
||||||
|
mov dword [edi + VMMDEV_REPORT_GUEST_INFO.interface_version], VMMDEV_VERSION
|
||||||
|
mov dword [edi + VMMDEV_REPORT_GUEST_INFO.os_type], VBOXOSTYPE_KOLIBRIOS
|
||||||
|
|
||||||
|
mov [vbox_device.guestinfo_virt], edi
|
||||||
|
mov eax, edi
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.guestinfo_phys], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] GuestInfo packet : virt=0x%x, phys=0x%x\n", \
|
||||||
|
[vbox_device.guestinfo_virt], eax
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 3) REPORT_GUEST_INFO2 (offset 128)
|
||||||
|
; =================================================================
|
||||||
|
lea edi, [ebx + VMMDEV_PKT_OFS_GUEST_INFO2]
|
||||||
|
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_REPORT_GUEST_INFO2
|
||||||
|
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_REPORT_GUEST_INFO2
|
||||||
|
; rc, reserved1, f_requestor уже 0 (обнулено)
|
||||||
|
; payload: версия заполняется из host_version в guest_info_2_report()
|
||||||
|
mov dword [edi + VMMDEV_REPORT_GUEST_INFO2.guest_info + VMMDEV_GUEST_INFO2.additions_features], VBOXGSTINFO2_F_REQUESTOR_INFO
|
||||||
|
; szName[128] — копируем строку версии
|
||||||
|
push edi ecx
|
||||||
|
lea edi, [edi + VMMDEV_REPORT_GUEST_INFO2.guest_info + VMMDEV_GUEST_INFO2.szName]
|
||||||
|
mov esi, vmmdev_version_string
|
||||||
|
mov ecx, VMMDEV_VERSION_STRING_LEN
|
||||||
|
rep movsb
|
||||||
|
pop ecx edi
|
||||||
|
|
||||||
|
lea eax, [ebx + VMMDEV_PKT_OFS_GUEST_INFO2]
|
||||||
|
mov [vbox_device.guestinfo2_virt], eax
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.guestinfo2_phys], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] GuestInfo2 packet : virt=0x%x, phys=0x%x\n", \
|
||||||
|
[vbox_device.guestinfo2_virt], eax
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 4) SET_GUEST_CAPABILITIES2 (offset 320)
|
||||||
|
; =================================================================
|
||||||
|
lea edi, [ebx + VMMDEV_PKT_OFS_CAPS]
|
||||||
|
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_SET_GUEST_CAPABILITIES2
|
||||||
|
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_SET_GUEST_CAPS
|
||||||
|
mov dword [edi + VMMDEV_SET_GUEST_CAPABILITIES2.or_mask], VBOXGUEST_GUEST_CAPS_OR_MASK
|
||||||
|
mov dword [edi + VMMDEV_SET_GUEST_CAPABILITIES2.not_mask], VBOXGUEST_GUEST_CAPS_NOT_MASK
|
||||||
|
|
||||||
|
mov [vbox_device.caps_virt], edi
|
||||||
|
mov eax, edi
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.caps_phys], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Caps packet : virt=0x%x, phys=0x%x\n", \
|
||||||
|
[vbox_device.caps_virt], eax
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 5) CTL_GUEST_FILTER_MASK (offset 384)
|
||||||
|
; =================================================================
|
||||||
|
lea edi, [ebx + VMMDEV_PKT_OFS_FILTER]
|
||||||
|
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_CTL_GUEST_FILTER_MASK
|
||||||
|
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_CTL_GUEST_FILTER_MASK
|
||||||
|
mov dword [edi + VMMDEV_CTL_GUEST_FILTER_MASK.or_mask], VBOXGUEST_EVENTS_OR_MASK
|
||||||
|
mov dword [edi + VMMDEV_CTL_GUEST_FILTER_MASK.not_mask], VBOXGUEST_EVENTS_NOT_MASK
|
||||||
|
|
||||||
|
mov [vbox_device.filter_virt], edi
|
||||||
|
mov eax, edi
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.filter_phys], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Filter packet : virt=0x%x, phys=0x%x\n", \
|
||||||
|
[vbox_device.filter_virt], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Filter: OR=0x%x, NOT=0x%x\n", \
|
||||||
|
[edi + VMMDEV_CTL_GUEST_FILTER_MASK.or_mask], \
|
||||||
|
[edi + VMMDEV_CTL_GUEST_FILTER_MASK.not_mask]
|
||||||
|
|
||||||
|
; =================================================================
|
||||||
|
; 6) ACKNOWLEDGE_EVENTS (offset 448)
|
||||||
|
; =================================================================
|
||||||
|
lea edi, [ebx + VMMDEV_PKT_OFS_ACK_EVENTS]
|
||||||
|
mov dword [edi + VMMDEV_HEADER.size], sizeof.VMMDEV_ACKNOWLEDGE_EVENTS
|
||||||
|
mov dword [edi + VMMDEV_HEADER.version], VMMDEV_REQUEST_HEADER_VERSION
|
||||||
|
mov dword [edi + VMMDEV_HEADER.request_type], VMMDEV_REQ_ACKNOWLEDGE_EVENTS
|
||||||
|
|
||||||
|
mov [vbox_device.events_virt], edi
|
||||||
|
mov eax, edi
|
||||||
|
invoke GetPhysAddr
|
||||||
|
mov [vbox_device.events_phys], eax
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] ACK packet : virt=0x%x, phys=0x%x\n", \
|
||||||
|
[vbox_device.events_virt], eax
|
||||||
|
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] Init packets success\n"
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.alloc_fail:
|
||||||
|
DEBUGF 2, "[VBoxGuest] [VMMDev] ERROR: KernelAlloc failed for VMMDev packets\n"
|
||||||
|
mov eax, VERR_NO_MEMORY
|
||||||
|
ret
|
||||||
|
endp
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; vmmdev_free_packets — Освободить страницу VMMDev пакетов
|
||||||
|
; =============================================================================
|
||||||
|
proc vmmdev_free_packets
|
||||||
|
cmp dword [vbox_device.vmmdev_packets_page], 0
|
||||||
|
je @f
|
||||||
|
invoke KernelFree, [vbox_device.vmmdev_packets_page]
|
||||||
|
mov dword [vbox_device.vmmdev_packets_page], 0
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
endp
|
||||||
12
vmmdev/vmmdev.inc
Normal file
12
vmmdev/vmmdev.inc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
; =============================================================================
|
||||||
|
; Модуль : VMMDev Aggregator
|
||||||
|
; Назначение : Подключение модулей протокола VMMDev
|
||||||
|
; Файл : vmmdev/vmmdev.inc
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
include 'vmmdev/packets.inc'
|
||||||
|
include 'vmmdev/core.inc'
|
||||||
|
include 'vmmdev/guest_info.inc'
|
||||||
|
include 'vmmdev/capabilities.inc'
|
||||||
|
include 'vmmdev/event_filter.inc'
|
||||||
|
include 'vmmdev/hypervisor.inc'
|
||||||
Reference in New Issue
Block a user