; $Id: SUPDrvA-os2.asm 22077 2009-08-07 16:01:57Z vboxsync $ ;; @file ; VBoxDrv - OS/2 assembly file, the first file in the link. ; ; ; Copyright (c) 2007 knut st. osmundsen ; ; Permission is hereby granted, free of charge, to any person ; obtaining a copy of this software and associated documentation ; files (the "Software"), to deal in the Software without ; restriction, including without limitation the rights to use, ; copy, modify, merge, publish, distribute, sublicense, and/or sell ; copies of the Software, and to permit persons to whom the ; Software is furnished to do so, subject to the following ; conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ; OTHER DEALINGS IN THE SOFTWARE. ; ;******************************************************************************* ;* Header Files * ;******************************************************************************* %define RT_INCL_16BIT_SEGMENTS %include "iprt/asmdefs.mac" ;******************************************************************************* ;* Structures and Typedefs * ;******************************************************************************* ;; ; Request packet header. struc PKTHDR .cb resb 1 .unit resb 1 .cmd resb 1 .status resw 1 .res1 resd 1 .link resd 1 endstruc ;; ; Init request packet - input. struc PKTINITIN .cb resb 1 .unit resb 1 .cmd resb 1 .status resw 1 .res1 resd 1 .link resd 1 .data_1 resb 1 .fpfnDevHlp resd 1 .fpszArgs resd 1 .data_2 resb 1 endstruc ;; ; Init request packet - output. struc PKTINITOUT .cb resb 1 .unit resb 1 .cmd resb 1 .status resw 1 .res1 resd 1 .link resd 1 .cUnits resb 1 ; block devs only. .cbCode16 resw 1 .cbData16 resw 1 .fpaBPBs resd 1 ; block devs only. .data_2 resb 1 endstruc ;; ; Open request packet. struc PKTOPEN .cb resb 1 .unit resb 1 .cmd resb 1 .status resw 1 .res1 resd 1 .link resd 1 .sfn resw 1 endstruc ;; ; Close request packet. struc PKTCLOSE .cb resb 1 .unit resb 1 .cmd resb 1 .status resw 1 .res1 resd 1 .link resd 1 .sfn resw 1 endstruc ;; ; IOCtl request packet. struc PKTIOCTL .cb resb 1 .unit resb 1 .cmd resb 1 .status resw 1 .res1 resd 1 .link resd 1 .cat resb 1 .fun resb 1 .pParm resd 1 .pData resd 1 .sfn resw 1 .cbParm resw 1 .cbData resw 1 endstruc ;; ; Read/Write request packet struc PKTRW .cb resb 1 .unit resb 1 .cmd resb 1 .status resw 1 .res1 resd 1 .link resd 1 .media resb 1 .PhysTrans resd 1 .cbTrans resw 1 .start resd 1 .sfn resw 1 endstruc ;; ; The two device headers. segment DATA16 ; Some devhdr.inc stuff. %define DEVLEV_3 0180h %define DEV_30 0800h %define DEV_CHAR_DEV 8000h %define DEV_16MB 0002h %define DEV_IOCTL2 0001h ; Some dhcalls.h stuff. %define DevHlp_VirtToLin 05bh %define DevHlp_SAVE_MESSAGE 03dh %define DevHlp_PhysToVirt 015h ; Fast IOCtl category, also defined in SUPDrvIOC.h %define SUP_CTL_CATEGORY_FAST 0c1h ;******************************************************************************* ;* External Symbols * ;******************************************************************************* extern KernThunkStackTo32 extern KernThunkStackTo16 extern DOS16OPEN extern DOS16CLOSE extern DOS16WRITE extern NAME(VBoxDrvInit) extern NAME(VBoxDrvOpen) extern NAME(VBoxDrvClose) extern NAME(VBoxDrvIOCtl) extern NAME(VBoxDrvIOCtlFast) ;; ; Device headers. The first one is the one we'll be opening and the ; latter is only used for 32-bit initialization. GLOBALNAME g_VBoxDrvHdr1 dw NAME(g_VBoxDrvHdr2) wrt DATA16 ; NextHeader.off dw DATA16 ; NextHeader.sel dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV; SDevAtt dw NAME(VBoxDrvEP) wrt CODE16 ; StrategyEP dw 0 ; InterruptEP db 'vboxdrv$' ; DevName dw 0 ; SDevProtCS dw 0 ; SDevProtDS dw 0 ; SDevRealCS dw 0 ; SDevRealDS dd DEV_16MB | DEV_IOCTL2 ; SDevCaps align 4 GLOBALNAME g_VBoxDrvHdr2 dd 0ffffffffh ; NextHeader (NIL) dw DEVLEV_3 | DEV_30 | DEV_CHAR_DEV; SDevAtt dw NAME(VBoxDrvInitEP) wrt CODE16 ; StrategyEP dw 0 ; InterruptEP db 'vboxdr1$' ; DevName dw 0 ; SDevProtCS dw 0 ; SDevProtDS dw 0 ; SDevRealCS dw 0 ; SDevRealDS dd DEV_16MB | DEV_IOCTL2 ; SDevCaps ;; Tristate 32-bit initialization indicator [0 = need init, -1 = init failed, 1 init succeeded]. ; Check in the open path of the primary driver. The secondary driver will ; open the primary one during it's init and thereby trigger the 32-bit init. GLOBALNAME g_fInitialized db 0 align 4 ;; Pointer to the device helper service routine ; This is set during the initialization of the 2nd device driver. GLOBALNAME g_fpfnDevHlp dd 0 ;; Where we write to the log. GLOBALNAME g_offLogHead dw 0 ;; Where we read from the log. GLOBALNAME g_offLogTail dw 0 ;; The size of the log. (power of two!) %define LOG_SIZE 16384 GLOBALNAME g_cchLogMax dw LOG_SIZE ;; The log buffer. GLOBALNAME g_szLog times LOG_SIZE db 0 ; ; The init data. ; segment DATA16_INIT GLOBALNAME g_InitDataStart ;; Far pointer to the device argument. g_fpszArgs: dd 0 %if 0 ;; Message table for the Save_Message device helper. GLOBALNAME g_MsgTab dw 1178 ; MsgId - 'MSG_REPLACEMENT_STRING'. dw 1 ; cMsgStrings dw NAME(g_szInitText) ; MsgStrings[0] dw seg NAME(g_szInitText) %else ;; Far pointer to DOS16WRITE (corrected set before called). ; Just a temporary hack to work around a wlink issue. GLOBALNAME g_fpfnDos16Write dw DOS16WRITE dw seg DOS16WRITE %endif ;; Size of the text currently in the g_szInitText buffer. GLOBALNAME g_cchInitText dw 0 ;; The max size of text that can fit into the g_szInitText buffer. GLOBALNAME g_cchInitTextMax dw 512 ;; The init text buffer. GLOBALNAME g_szInitText times 512 db 0 ; ; The 16-bit code segment. ; segment CODE16 ;; ; The strategy entry point (vboxdrv$). ; ; ss:bx -> request packet ; ds:si -> device header ; ; Can clobber any registers it likes except SP. ; BEGINPROC VBoxDrvEP push ebp mov ebp, esp push es ; bp - 2 push bx ; bp - 4 and sp, 0fffch ; ; Check for the most frequent first. ; cmp byte [es:bx + PKTHDR.cmd], 10h ; Generic IOCtl jne near VBoxDrvEP_NotGenIOCtl ; ; Generic I/O Control Request. ; VBoxDrvEP_GenIOCtl: ; Fast IOCtl? cmp byte [es:bx + PKTIOCTL.cat], SUP_CTL_CATEGORY_FAST jne VBoxDrvEP_GenIOCtl_Other ; ; Fast IOCtl. ; DECLASM(int) VBoxDrvIOCtlFast(uint16_t sfn, uint8_t iFunction) ; VBoxDrvEP_GenIOCtl_Fast: ; function. movzx edx, byte [es:bx + PKTIOCTL.fun] push edx ; 04h ; system file number. movzx eax, word [es:bx + PKTIOCTL.sfn] push eax ; 00h ; go to the 32-bit code ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Fast_32) wrt FLAT db 066h db 0eah dd NAME(VBoxDrvEP_GenIOCtl_Fast_32) ;wrt FLAT dw TEXT32 wrt FLAT segment TEXT32 GLOBALNAME VBoxDrvEP_GenIOCtl_Fast_32 ; switch stack to 32-bit. mov ax, DATA32 wrt FLAT mov ds, ax mov es, ax call KernThunkStackTo32 ; call the C code (don't cleanup the stack). call NAME(VBoxDrvIOCtlFast) ; switch back the stack. push eax call KernThunkStackTo16 pop eax ; jump back to the 16-bit code. ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Fast_16) wrt CODE16 db 066h db 0eah dw NAME(VBoxDrvEP_GenIOCtl_Fast_16) wrt CODE16 dw CODE16 segment CODE16 GLOBALNAME VBoxDrvEP_GenIOCtl_Fast_16 les bx, [bp - 4] ; Reload the packet pointer. or eax, eax jnz near VBoxDrvEP_GeneralFailure ; setup output stuff. xor eax, eax mov [es:bx + PKTIOCTL.cbParm], eax ; update cbParm and cbData. mov word [es:bx + PKTHDR.status], 00100h ; done, ok. mov sp, bp pop ebp retf ; ; Other IOCtl (slow) ; VBoxDrvEP_GenIOCtl_Other: mov eax, [es:bx + PKTIOCTL.cbParm] ; Load cbParm and cbData push eax ; 1eh - in/out data size. ; 1ch - in/out parameter size. push edx ; 18h - pointer to data size (filled in later). push ecx ; 14h - pointer to param size (filled in later). ; pData (convert to flat 32-bit) mov ax, word [es:bx + PKTIOCTL.pData + 2] ; selector cmp ax, 3 ; <= 3 -> nil selector... jbe .no_data movzx esi, word [es:bx + PKTIOCTL.pData] ; offset mov dl, DevHlp_VirtToLin call far [NAME(g_fpfnDevHlp)] jc near VBoxDrvEP_GeneralFailure jmp .finish_data .no_data: xor eax, eax .finish_data: push eax ; 10h ; pParm (convert to flat 32-bit) mov ax, word [es:bx + PKTIOCTL.pParm + 2] ; selector cmp ax, 3 ; <= 3 -> nil selector... jbe .no_parm movzx esi, word [es:bx + PKTIOCTL.pParm] ; offset mov dl, DevHlp_VirtToLin call far [NAME(g_fpfnDevHlp)] jc near VBoxDrvEP_GeneralFailure jmp .finish_parm .no_parm: xor eax, eax .finish_parm: push eax ; 0ch ; function. movzx edx, byte [es:bx + PKTIOCTL.fun] push edx ; 08h ; category. movzx ecx, byte [es:bx + PKTIOCTL.cat] push ecx ; 04h ; system file number. movzx eax, word [es:bx + PKTIOCTL.sfn] push eax ; 00h ; go to the 32-bit code ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Other_32) wrt FLAT db 066h db 0eah dd NAME(VBoxDrvEP_GenIOCtl_Other_32) ;wrt FLAT dw TEXT32 wrt FLAT segment TEXT32 GLOBALNAME VBoxDrvEP_GenIOCtl_Other_32 ; switch stack to 32-bit. mov ax, DATA32 wrt FLAT mov ds, ax mov es, ax call KernThunkStackTo32 ; update in/out parameter pointers lea eax, [esp + 1ch] mov [esp + 14h], eax lea edx, [esp + 1eh] mov [esp + 18h], edx ; call the C code (don't cleanup the stack). call NAME(VBoxDrvIOCtl) ; switch back the stack. push eax call KernThunkStackTo16 pop eax ; jump back to the 16-bit code. ;jmp far dword NAME(VBoxDrvEP_GenIOCtl_Other_16) wrt CODE16 db 066h db 0eah dw NAME(VBoxDrvEP_GenIOCtl_Other_16) wrt CODE16 dw CODE16 segment CODE16 GLOBALNAME VBoxDrvEP_GenIOCtl_Other_16 les bx, [bp - 4] ; Reload the packet pointer. or eax, eax jnz near VBoxDrvEP_GeneralFailure ; setup output stuff. mov edx, esp mov eax, [ss:edx + 1ch] ; output sizes. mov [es:bx + PKTIOCTL.cbParm], eax ; update cbParm and cbData. mov word [es:bx + PKTHDR.status], 00100h ; done, ok. mov sp, bp pop ebp retf ; ; Less Performance Critical Requests. ; VBoxDrvEP_NotGenIOCtl: cmp byte [es:bx + PKTHDR.cmd], 0dh ; Open je VBoxDrvEP_Open cmp byte [es:bx + PKTHDR.cmd], 0eh ; Close je VBoxDrvEP_Close cmp byte [es:bx + PKTHDR.cmd], 00h ; Init je VBoxDrvEP_Init cmp byte [es:bx + PKTHDR.cmd], 04h ; Read je near VBoxDrvEP_Read jmp near VBoxDrvEP_NotSupported ; ; Open Request. w/ ring-0 init. ; VBoxDrvEP_Open: cmp byte [NAME(g_fInitialized)], 1 jne VBoxDrvEP_OpenOther ; First argument, the system file number. movzx eax, word [es:bx + PKTOPEN.sfn] push eax ; go to the 32-bit code ;jmp far dword NAME(VBoxDrvEP_Open_32) wrt FLAT db 066h db 0eah dd NAME(VBoxDrvEP_Open_32) ;wrt FLAT dw TEXT32 wrt FLAT segment TEXT32 GLOBALNAME VBoxDrvEP_Open_32 ; switch stack to 32-bit. mov ax, DATA32 wrt FLAT mov ds, ax mov es, ax call KernThunkStackTo32 ; call the C code. call NAME(VBoxDrvOpen) ; switch back the stack. push eax call KernThunkStackTo16 pop eax ; jump back to the 16-bit code. ;jmp far dword NAME(VBoxDrvEP_Open_32) wrt CODE16 db 066h db 0eah dw NAME(VBoxDrvEP_Open_16) wrt CODE16 dw CODE16 segment CODE16 GLOBALNAME VBoxDrvEP_Open_16 les bx, [bp - 4] ; Reload the packet pointer. or eax, eax jnz near VBoxDrvEP_GeneralFailure mov word [es:bx + PKTHDR.status], 00100h ; done, ok. jmp near VBoxDrvEP_Done ; Initializing or failed init? VBoxDrvEP_OpenOther: cmp byte [NAME(g_fInitialized)], 0 jne VBoxDrvEP_OpenFailed mov byte [NAME(g_fInitialized)], -1 call NAME(VBoxDrvRing0Init) cmp byte [NAME(g_fInitialized)], 1 je VBoxDrvEP_Open VBoxDrvEP_OpenFailed: mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed. jmp near VBoxDrvEP_Done ; ; Close Request. ; VBoxDrvEP_Close: ; First argument, the system file number. movzx eax, word [es:bx + PKTOPEN.sfn] push eax ; go to the 32-bit code ;jmp far dword NAME(VBoxDrvEP_Close_32) wrt FLAT db 066h db 0eah dd NAME(VBoxDrvEP_Close_32) ;wrt FLAT dw TEXT32 wrt FLAT segment TEXT32 GLOBALNAME VBoxDrvEP_Close_32 ; switch stack to 32-bit. mov ax, DATA32 wrt FLAT mov ds, ax mov es, ax call KernThunkStackTo32 ; call the C code. call NAME(VBoxDrvClose) ; switch back the stack. push eax call KernThunkStackTo16 pop eax ; jump back to the 16-bit code. ;jmp far dword NAME(VBoxDrvEP_Close_32) wrt CODE16 db 066h db 0eah dw NAME(VBoxDrvEP_Close_16) wrt CODE16 dw CODE16 segment CODE16 GLOBALNAME VBoxDrvEP_Close_16 les bx, [bp - 4] ; Reload the packet pointer. or eax, eax jnz near VBoxDrvEP_GeneralFailure mov word [es:bx + PKTHDR.status], 00100h ; done, ok. jmp near VBoxDrvEP_Done ; ; Init Request. ; The other driver header will do this. ; VBoxDrvEP_Init: mov word [es:bx + PKTHDR.status], 00100h ; done, ok. mov byte [es:bx + PKTINITOUT.cUnits], 0 mov word [es:bx + PKTINITOUT.cbCode16], NAME(g_InitCodeStart) wrt CODE16 mov word [es:bx + PKTINITOUT.cbData16], NAME(g_InitDataStart) wrt DATA16 mov dword [es:bx + PKTINITOUT.fpaBPBs], 0 jmp near VBoxDrvEP_Done ; ; Read Request. ; Return log data. ; VBoxDrvEP_Read: ; Any log data available? xor dx, dx mov ax, [NAME(g_offLogTail)] cmp ax, [NAME(g_offLogHead)] jz near .log_done ; create a temporary mapping of the physical buffer. Docs claims it trashes nearly everything... push ebp mov cx, [es:bx + PKTRW.cbTrans] push cx mov ax, [es:bx + PKTRW.PhysTrans + 2] mov bx, [es:bx + PKTRW.PhysTrans] mov dh, 1 mov dl, DevHlp_PhysToVirt call far [NAME(g_fpfnDevHlp)] pop bx ; bx = cbTrans pop ebp jc near .log_phystovirt_failed ; es:di -> the output buffer. ; setup the copy operation. mov ax, [NAME(g_offLogTail)] xor dx, dx ; dx tracks the number of bytes copied. .log_loop: mov cx, [NAME(g_offLogHead)] cmp ax, cx je .log_done jb .log_loop_before mov cx, LOG_SIZE .log_loop_before: ; cx = end offset sub cx, ax ; cx = sequential bytes to copy. cmp cx, bx jbe .log_loop_min mov cx, bx ; output buffer is smaller than available data. .log_loop_min: mov si, NAME(g_szLog) add si, ax ; ds:si -> the log buffer. add dx, cx ; update output counter add ax, cx ; calc new offLogTail and ax, LOG_SIZE - 1 rep movsb ; do the copy mov [NAME(g_offLogTail)], ax ; commit the read. jmp .log_loop .log_done: les bx, [bp - 4] ; Reload the packet pointer. mov word [es:bx + PKTRW.cbTrans], dx mov word [es:bx + PKTHDR.status], 00100h ; done, ok. jmp near VBoxDrvEP_Done .log_phystovirt_failed: les bx, [bp - 4] ; Reload the packet pointer. jmp VBoxDrvEP_GeneralFailure ; ; Return 'unknown command' error. ; VBoxDrvEP_NotSupported: mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. jmp VBoxDrvEP_Done ; ; Return 'general failure' error. ; VBoxDrvEP_GeneralFailure: mov word [es:bx + PKTHDR.status], 0810ch ; error, done, general failure. jmp VBoxDrvEP_Done ; ; Non-optimized return path. ; VBoxDrvEP_Done: mov sp, bp pop ebp retf ENDPROC VBoxDrvEP ;; ; The helper device entry point. ; ; This is only used to do the DosOpen on the main driver so we can ; do ring-3 init and report failures. ; GLOBALNAME VBoxDrvInitEP ; The only request we're servicing is the 'init' one. cmp word [es:bx + PKTHDR.cmd], 0 je near NAME(VBoxDrvInitEPServiceInitReq) ; Ok, it's not the init request, just fail it. mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. retf ; ; The 16-bit init code. ; segment CODE16_INIT GLOBALNAME g_InitCodeStart ;; The device name for DosOpen. g_szDeviceName: db '\DEV\vboxdrv$', 0 ; icsdebug can't see where stuff starts otherwise. (kDevTest) int3 int3 int3 int3 int3 int3 ;; ; The Ring-3 init code. ; BEGINPROC VBoxDrvInitEPServiceInitReq push ebp mov ebp, esp push es ; bp - 2 push sp ; bp - 4 push -1 ; bp - 6: hfOpen push 0 ; bp - 8: usAction and sp, 0fffch ; check for the init package. cmp word [es:bx + PKTHDR.cmd], 0 jne near .not_init ; ; Copy the data out of the init packet. ; mov eax, [es:bx + PKTINITIN.fpfnDevHlp] mov [NAME(g_fpfnDevHlp)], eax mov edx, [es:bx + PKTINITIN.fpszArgs] mov [g_fpszArgs], edx ; ; Open the first driver, close it, and check status. ; ; APIRET _Pascal DosOpen(PSZ pszFname, PHFILE phfOpen, PUSHORT pusAction, ; ULONG ulFSize, USHORT usAttr, USHORT fsOpenFlags, ; USHORT fsOpenMode, ULONG ulReserved); push seg g_szDeviceName ; pszFname push g_szDeviceName push ss ; phfOpen lea dx, [bp - 6] push dx push ss ; pusAction lea dx, [bp - 8] push dx push dword 0 ; ulFSize push 0 ; usAttr = FILE_NORMAL push 1 ; fsOpenFlags = FILE_OPEN push 00040h ; fsOpenMode = OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY push dword 0 ; ulReserved call far DOS16OPEN push ax ; Quickly flush any text. call NAME(VBoxDrvInitFlushText) pop ax or ax, ax jnz .done_err ; APIRET APIENTRY DosClose(HFILE hf); mov cx, [bp - 6] push cx call far DOS16CLOSE or ax, ax jnz .done_err ; This can't happen (I hope). ; ; Ok, we're good. ; mov word [es:bx + PKTHDR.status], 00100h ; done, ok. mov byte [es:bx + PKTINITOUT.cUnits], 0 mov word [es:bx + PKTINITOUT.cbCode16], NAME(g_InitCodeStart) wrt CODE16 mov word [es:bx + PKTINITOUT.cbData16], NAME(g_InitDataStart) wrt DATA16 mov dword [es:bx + PKTINITOUT.fpaBPBs], 0 jmp .done ; ; Init failure. ; .done_err: mov word [es:bx + PKTHDR.status], 0810fh ; error, done, init failed. mov byte [es:bx + PKTINITOUT.cUnits], 0 mov word [es:bx + PKTINITOUT.cbCode16], 0 mov word [es:bx + PKTINITOUT.cbData16], 0 mov dword [es:bx + PKTINITOUT.fpaBPBs], 0 jmp .done ; ; Not init, return 'unknown command'. ; .not_init: mov word [es:bx + PKTHDR.status], 08103h ; error, done, unknown command. jmp .done ; ; Request done. ; .done: mov sp, bp pop ebp retf ENDPROC VBoxDrvInitEPServiceInitReq ;; ; The Ring-0 init code. ; BEGINPROC VBoxDrvRing0Init push es push esi push ebp mov ebp, esp and sp, 0fffch ; ; Thunk the argument string pointer first. ; movzx esi, word [g_fpszArgs] ; offset mov ax, [g_fpszArgs + 2] ; selector mov dl, DevHlp_VirtToLin call far [NAME(g_fpfnDevHlp)] jc near VBoxDrvRing0Init_done ; eax is non-zero on failure (can't happen) push eax ; 00h - pszArgs (for VBoxDrvInit). ; ; Do 16-bit init? ; ; ; Do 32-bit init ; ;jmp far dword NAME(VBoxDrvRing0Init_32) wrt FLAT db 066h db 0eah dd NAME(VBoxDrvRing0Init_32) ;wrt FLAT dw TEXT32 wrt FLAT segment TEXT32 GLOBALNAME VBoxDrvRing0Init_32 ; switch stack to 32-bit. mov ax, DATA32 wrt FLAT mov ds, ax mov es, ax call KernThunkStackTo32 ; call the C code. call NAME(VBoxDrvInit) ; switch back the stack and reload ds. push eax call KernThunkStackTo16 pop eax mov dx, seg NAME(g_fInitialized) mov ds, dx ; jump back to the 16-bit code. ;jmp far dword NAME(VBoxDrvRing0Init_16) wrt CODE16 db 066h db 0eah dw NAME(VBoxDrvRing0Init_16) wrt CODE16 dw CODE16_INIT segment CODE16_INIT GLOBALNAME VBoxDrvRing0Init_16 ; check the result and set g_fInitialized on success. or eax, eax jnz VBoxDrvRing0Init_done mov byte [NAME(g_fInitialized)], 1 VBoxDrvRing0Init_done: mov sp, bp pop ebp pop esi pop es ret ENDPROC VBoxDrvRing0Init ;; ; Flush any text in the text buffer. ; BEGINPROC VBoxDrvInitFlushText push bp mov bp, sp ; Anything in the buffer? mov ax, [NAME(g_cchInitText)] or ax, ax jz .done %if 1 ; Write it to STDOUT. ; APIRET _Pascal DosWrite(HFILE hf, PVOID pvBuf, USHORT cbBuf, PUSHORT pcbBytesWritten); push ax ; bp - 2 : cbBytesWritten mov cx, sp push 1 ; STDOUT push seg NAME(g_szInitText) ; pvBuf push NAME(g_szInitText) push ax ; cbBuf push ss ; pcbBytesWritten push cx %if 0 ; wlink generates a non-aliased fixup here which results in 16-bit offset with the flat 32-bit selector. call far DOS16WRITE %else ; convert flat pointer to a far pointer using the tiled algorithm. push ds mov ax, DATA32 wrt FLAT mov ds, ax mov eax, g_pfnDos16Write wrt FLAT movzx eax, word [eax + 2] ; High word of the flat address (in DATA32). shl ax, 3 or ax, 0007h pop ds mov [NAME(g_fpfnDos16Write) + 2], ax ; Update the selector (in DATA16_INIT). ; do the call call far [NAME(g_fpfnDos16Write)] %endif %else ; alternative workaround for the wlink issue. ; Use the save message devhlp. push esi push ebx xor bx, bx mov si, NAME(g_MsgTab) mov dx, seg NAME(g_MsgTab) mov ds, dx mov dl, DevHlp_SAVE_MESSAGE call far [NAME(g_fpfnDevHlp)] pop ebx pop esi %endif ; Empty the buffer. mov word [NAME(g_cchInitText)], 0 mov byte [NAME(g_szInitText)], 0 .done: mov sp, bp pop bp ret ENDPROC VBoxDrvInitFlushText ;; ; This must be present segment DATA32 g_pfnDos16Write: dd DOS16WRITE ; flat