Index: trunk/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp
===================================================================
--- trunk/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp	(revision 1197)
+++ trunk/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.cpp	(revision 1197)
@@ -0,0 +1,422 @@
+/* $Id$ */
+/** @file
+ * VBoxDrv - OS/2 specifics.
+ */
+
+/*
+ * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * 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                                                               *
+*******************************************************************************/
+#include <os2ddk/bsekee.h>
+#undef RT_MAX
+
+#include "SUPDRV.h"
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/**
+ * Device extention & session data association structure.
+ */
+static SUPDRVDEVEXT         g_DevExt;
+/** Spinlock protecting g_apSessionHashTab. */
+static RTSPINLOCK           g_Spinlock = NIL_RTSPINLOCK;
+/** Hash table */
+static PSUPDRVSESSION       g_apSessionHashTab[19];
+/** Calculates the index into g_apSessionHashTab.*/
+#define SESSION_HASH(sfn) ((sfn) % RT_ELEMENTS(g_apSessionHashTab))
+
+__BEGIN_DECLS
+/* Defined in SUPDrvA-os2.asm */
+extern uint16_t             g_offLogHead;
+extern uint16_t volatile    g_offLogTail;
+extern uint16_t const       g_cchLogMax;
+extern char                 g_szLog[];
+/* (init only:) */
+extern char                 g_szInitText[];
+extern uint16_t             g_cchInitText;
+extern uint16_t             g_cchInitTextMax;
+__END_DECLS
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+
+
+/**
+ * 32-bit Ring-0 initialization.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param   pszArgs     Pointer to the device arguments.
+ */
+DECLASM(int) VBoxDrvInit(const char *pszArgs)
+{
+    dprintf(("VBoxDrvInit: pszArgs=%s\n", pszArgs));
+
+    /*
+     * Initialize the runtime.
+     */
+    int rc = RTR0Init(0);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Initialize the device extension.
+         */
+        rc = supdrvInitDevExt(&g_DevExt);
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Initialize the session hash table.
+             */
+            rc = RTSpinlockCreate(&g_Spinlock);
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * Process the commandline. Later.
+                 */
+                bool fVerbose = true;
+
+                /*
+                 * Success
+                 */
+                if (fVerbose)
+                {
+                    strcpy(&g_szInitText[0],
+                           "\r\n"
+                           "VirtualBox.org Support Driver for OS/2 version " VBOX_VERSION_STRING "\r\n"
+                           "Copyright (C) 2007 Knut St. Osmundsen\r\n"
+                           "Copyright (C) 2007 InnoTek Systemberatung GmbH\r\n");
+                    g_cchInitText = strlen(&g_szInitText[0]);
+                }
+                return VINF_SUCCESS;
+            }
+            g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: RTSpinlockCreate failed, rc=%Vrc\n", rc);
+            supdrvDeleteDevExt(&g_DevExt);
+        }
+        else
+            g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: supdrvInitDevExt failed, rc=%Vrc\n", rc);
+        RTR0Term();
+    }
+    else
+        g_cchInitText = RTStrPrintf(&g_szInitText[0], g_cchInitTextMax, "VBoxDrv.sys: RTR0Init failed, rc=%Vrc\n", rc);
+    return rc;
+}
+
+
+DECLASM(int) VBoxDrvOpen(uint16_t sfn)
+{
+    int                 rc;
+    PSUPDRVSESSION      pSession;
+
+    /*
+     * Create a new session.
+     */
+    rc = supdrvCreateSession(&g_DevExt, &pSession);
+    if (RT_SUCCESS(rc))
+    {
+        pSession->Process = RTProcSelf();
+        pSession->R0Process = RTR0ProcHandleSelf();
+        pSession->sfn = sfn;
+
+        /*
+         * Insert it into the hash table.
+         */
+        unsigned iHash = SESSION_HASH(sfn);
+        RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
+        RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
+        pSession->pNextHash = g_apSessionHashTab[iHash];
+        g_apSessionHashTab[iHash] = pSession;
+        RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
+    }
+
+    dprintf(("VBoxDrvOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, (int)RTProcSelf()));
+    return rc;
+}
+
+
+DECLASM(int) VBoxDrvClose(uint16_t sfn)
+{
+    dprintf(("VBoxDrvClose: pid=%d sfn=%d\n", (int)RTProcSelf(), sfn));
+
+    /*
+     * Remove from the hash table.
+     */
+    PSUPDRVSESSION  pSession;
+    const RTPROCESS Process = RTProcSelf();
+    const unsigned  iHash = SESSION_HASH(sfn);
+    RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
+    RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
+
+    pSession = g_apSessionHashTab[iHash];
+    if (pSession)
+    {
+        if (    pSession->sfn == sfn
+            &&  pSession->Process == Process)
+        {
+            g_apSessionHashTab[iHash] = pSession->pNextHash;
+            pSession->pNextHash = NULL;
+        }
+        else
+        {
+            PSUPDRVSESSION pPrev = pSession;
+            pSession = pSession->pNextHash;
+            while (pSession)
+            {
+                if (    pSession->sfn == sfn
+                    &&  pSession->Process == Process)
+                {
+                    pPrev->pNextHash = pSession->pNextHash;
+                    pSession->pNextHash = NULL;
+                    break;
+                }
+
+                /* next */
+                pPrev = pSession;
+                pSession = pSession->pNextHash;
+            }
+        }
+    }
+    RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
+    if (!pSession)
+    {
+        OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d sfn=%d\n", (int)Process, sfn));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Close the session.
+     */
+    supdrvCloseSession(&g_DevExt, pSession);
+    return 0;
+}
+
+
+DECLASM(int) VBoxDrvIOCtlFast(uint16_t sfn, uint8_t iFunction, int32_t *prc)
+{
+    /*
+     * Find the session.
+     */
+    RTSPINLOCKTMP       Tmp = RTSPINLOCKTMP_INITIALIZER;
+    const RTPROCESS     Process = RTProcSelf();
+    const unsigned      iHash = SESSION_HASH(sfn);
+    PSUPDRVSESSION      pSession;
+
+    RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
+    pSession = g_apSessionHashTab[iHash];
+    if (pSession && pSession->Process != Process)
+    {
+        do pSession = pSession->pNextHash;
+        while (     pSession
+               &&   (   pSession->sfn != sfn
+                     || pSession->Process != Process));
+    }
+    RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
+    if (RT_UNLIKELY(!pSession))
+    {
+        OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Dispatch the fast IOCtl.
+     */
+    *prc = supdrvIOCtlFast(iFunction, &g_DevExt, pSession);
+    return 0;
+}
+
+
+DECLASM(int) VBoxDrvIOCtl(uint16_t sfn, uint8_t iCat, uint8_t iFunction, void *pvParm, void *pvData, uint16_t *pcbParm, uint16_t *pcbData)
+{
+    /*
+     * Find the session.
+     */
+    RTSPINLOCKTMP       Tmp = RTSPINLOCKTMP_INITIALIZER;
+    const RTPROCESS     Process = RTProcSelf();
+    const unsigned      iHash = SESSION_HASH(sfn);
+    PSUPDRVSESSION      pSession;
+
+    RTSpinlockAcquireNoInts(g_Spinlock, &Tmp);
+    pSession = g_apSessionHashTab[iHash];
+    if (pSession && pSession->Process != Process)
+    {
+        do pSession = pSession->pNextHash;
+        while (     pSession
+               &&   (   pSession->sfn != sfn
+                     || pSession->Process != Process));
+    }
+    RTSpinlockReleaseNoInts(g_Spinlock, &Tmp);
+    if (!pSession)
+    {
+        OSDBGPRINT(("VBoxDrvIoctl: WHUT?!? pSession == NULL! This must be a mistake... pid=%d\n", (int)Process));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Verify the category and dispatch the IOCtl.
+     */
+    if (RT_LIKELY(iCat == SUP_CTL_CATEGORY))
+    {
+        dprintf(("VBoxDrvIOCtl: pSession=%p iFunction=%#x pvParm=%p pvData=%p *pcbParm=%d *pcbData=%d\n", pSession, iFunction, pvParm, pvData, *pcbParm, *pcbData));
+        Assert(pvParm);
+        Assert(!pvData);
+
+        /*
+         * Lock the buffers.
+         */
+        PSUPDRVIOCTLDATA pArgs = (PSUPDRVIOCTLDATA)pvParm;
+        AssertReturn(*pcbParm == sizeof(*pArgs), VERR_INVALID_PARAMETER);
+        KernVMLock_t ParmLock;
+        int rc = KernVMLock(VMDHL_WRITE, pvParm, *pcbParm, &ParmLock, (KernPageList_t *)-1, NULL);
+        AssertMsgReturn(!rc, ("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pvParm, *pcbParm, &ParmLock, rc), VERR_LOCK_FAILED);
+
+        /* lock the in and out buffers. */
+        KernVMLock_t    InLock;
+        bool            fInLocked = pArgs->pvIn && pArgs->cbIn;
+        if (fInLocked)
+        {
+            rc = KernVMLock(VMDHL_WRITE, pArgs->pvIn, pArgs->cbIn, &InLock, (KernPageList_t *)-1, NULL);
+            if (rc)
+            {
+                AssertMsgFailed(("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pArgs->pvIn, pArgs->cbIn, &InLock, rc));
+                KernVMUnlock(&ParmLock);
+                return VERR_LOCK_FAILED;
+            }
+        }
+
+        KernVMLock_t    OutLock;
+        bool            fOutLocked = pArgs->pvOut && pArgs->cbOut;
+        if (fOutLocked)
+        {
+            rc = KernVMLock(VMDHL_WRITE, pArgs->pvOut, pArgs->cbOut, &OutLock, (KernPageList_t *)-1, NULL);
+            if (rc)
+            {
+                AssertMsgFailed(("KernVMLock(VMDHL_WRITE, %p, %#x, &p, NULL, NULL) -> %d\n", pArgs->pvOut, pArgs->cbOut, &OutLock, rc));
+                KernVMUnlock(&ParmLock);
+                if (fInLocked)
+                    KernVMUnlock(&InLock);
+                return VERR_LOCK_FAILED;
+            }
+        }
+
+        /*
+         * Process the IOCtl.
+         */
+        unsigned cbReturned = 0;
+        pArgs->rc = rc = supdrvIOCtl(iFunction, &g_DevExt, pSession, pArgs->pvIn, pArgs->cbIn, pArgs->pvOut, pArgs->cbOut, &cbReturned);
+
+        /*
+         * Unlock the buffers.
+         */
+        if (fOutLocked)
+        {
+            int rc2 = KernVMUnlock(&OutLock);
+            AssertMsg(!rc2, ("rc2=%d\n", rc2));
+        }
+        if (fInLocked)
+        {
+            int rc2 = KernVMUnlock(&InLock);
+            AssertMsg(!rc2, ("rc2=%d\n", rc2));
+        }
+        int rc2 = KernVMUnlock(&ParmLock);
+        AssertMsg(!rc2, ("rc2=%d\n", rc2));
+
+        dprintf2(("VBoxDrvIOCtl: returns VINF_SUCCESS / %d\n", rc));
+        return VINF_SUCCESS;
+    }
+    return VERR_NOT_SUPPORTED;
+}
+
+
+void VBOXCALL   supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession)
+{
+    NOREF(pObj);
+    NOREF(pSession);
+}
+
+
+bool VBOXCALL   supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc)
+{
+    NOREF(pObj);
+    NOREF(pSession);
+    NOREF(pszObjName);
+    NOREF(prc);
+    return false;
+}
+
+
+/**
+ * Callback for writing to the log buffer.
+ *
+ * @returns number of bytes written.
+ * @param   pvArg       Unused.
+ * @param   pachChars   Pointer to an array of utf-8 characters.
+ * @param   cbChars     Number of bytes in the character array pointed to by pachChars.
+ */
+static DECLCALLBACK(size_t) VBoxDrvLogOutput(void *pvArg, const char *pachChars, size_t cbChars)
+{
+    size_t cchWritten = 0;
+    while (cbChars-- > 0)
+    {
+        const uint16_t offLogHead = g_offLogHead;
+        const uint16_t offLogHeadNext = (offLogHead + 1) & (g_cchLogMax - 1);
+        if (offLogHeadNext == g_offLogTail)
+            break; /* no */
+        g_szLog[offLogHead] = *pachChars++;
+        g_offLogHead = offLogHeadNext;
+        cchWritten++;
+    }
+    return cchWritten;
+}
+
+
+SUPR0DECL(int) SUPR0Printf(const char *pszFormat, ...)
+{
+    va_list va;
+
+#if 0 //def DEBUG_bird
+    va_start(va, pszFormat);
+    RTLogComPrintfV(pszFormat, va);
+    va_end(va);
+#endif
+
+    va_start(va, pszFormat);
+    int cch = RTLogFormatV(VBoxDrvLogOutput, NULL, pszFormat, va);
+    va_end(va);
+
+    return cch;
+}
Index: trunk/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def
===================================================================
--- trunk/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def	(revision 1197)
+++ trunk/src/VBox/HostDrivers/Support/os2/SUPDrv-os2.def	(revision 1197)
@@ -0,0 +1,39 @@
+;; @file
+; VBoxDrv - OS/2 definition file.
+;
+
+;
+; Copyright (C) 2007 InnoTek Systemberatung GmbH
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License as published by the Free Software Foundation,
+; in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+; distribution. VirtualBox OSE is distributed in the hope that it will
+; be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; If you received this file as part of a commercial VirtualBox
+; distribution, then only the terms of your commercial VirtualBox
+; license agreement apply instead of the previous paragraph.
+;
+
+
+PHYSICAL DEVICE VBoxDrv
+DESCRIPTION 'VirtualBox.org Support Driver for OS/2.'
+CODE PRELOAD EXECUTEREAD
+DATA PRELOAD
+; We're using wlink.exe, so this doesn't work.
+;SEGMENTS
+;    DATA16              class 'FAR_DATA'
+;    DATA16_INIT         class 'FAR_DATA'
+;
+;    CODE16              class 'CODE'
+;    CODE16_INIT         class 'CODE'
+;
+;    CODE32              class 'CODE'
+;    TEXT32              class 'CODE'
+;
+;    DATA32              class 'DATA'
+;    BSS32               class 'BSS'
+
Index: trunk/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm
===================================================================
--- trunk/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm	(revision 1197)
+++ trunk/src/VBox/HostDrivers/Support/os2/SUPDrvA-os2.asm	(revision 1197)
@@ -0,0 +1,981 @@
+; $Id$
+;; @file
+; VBoxDrv - OS/2 assembly file, the first file in the link.
+;
+
+;
+; Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+;
+; 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, uint16_t *pcbParm)
+    ;
+VBoxDrvEP_GenIOCtl_Fast:
+    mov     ax, [es:bx + PKTIOCTL.pData + 2]        ; LDT selector to flat address.
+    shr     ax, 3
+    shl     eax, 16
+    mov     ax, [es:bx + PKTIOCTL.pData]
+    push    eax                                     ; 08h - pointer to the rc buffer.
+
+    ; 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.
+    mov     edx, esp
+    mov     eax, [ss:edx + 0ch]                     ; 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
+
+    ;
+    ; 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
+
+
