home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-10-01 | 42.7 KB | 1,125 lines |
- VIPX.386 Description, Limitations, Configuration and Programming Document
-
- Current Version: 1.19
- Date: 940524
-
- Table of Contents
- -----------------
- I. Description
- II. Limitations
- A. Windows 3.X Versions
- B. Version Compatibility with Dedicated IPX
- C. Packet Size Limitations
- D. Memory Manager Limitations
- III. System Settings in the SYSTEM.INI File
- A. [386Enh] Section
- 1. TimerCriticalSection
- B. [VIPX] Section
- 1. VipxMappingPages
- 2. VipxFailOverSizedPackets
- 3. VpicdFix
- 4. AutoIrqVirtualize
- 5. VirtualizeIrq[0-F]
- 6. VipxErrorMessages
- 7. VipxWarningMessages
- 8. VipxOutDebugStrOnErrors
- 9. VipxOutDebugStrOnWarnings
- 10. VipxBreakOnErrors
- 11. VipxBreakOnWarnings
- IV. Programming to the VIPX Interface
- A. Getting the VIPX API Entry Point
- B. Memory Management Issues
- C. APIs Specific to VIPX.386
- D. Handling Reentrancy Issues
- E. Using NWIPXSPX.DLL
- F. Windows Application Resource Cleanup
- G. Using GlobalDOSAlloc()
- V. Global DOS TSRs using IPX/SPX
-
-
- I. Description
- ---------------
-
- VIPX.386 is a Windows 3.X virtualization driver for IPXODI.COM
- driver. It virtualizes requests to the globally loaded IPX driver.
- When a request is made to IPX, VIPX will allocate a request buffer in
- the system's global memory, copy the original request to the global
- buffer and give the global request to IPX. When the global request
- completes, IPX will call VIPX. VIPX will then copy any results back
- to the original request buffer and call the application which
- submitted the request.
-
-
- II. Limitations
- ----------------
-
-
- II.A. Windows Versions
- -----------------------
-
- VIPX supports Windows versions 3.0, 3.0a and 3.1. However, there is
- some functionality which VIPX will only support in Windows 3.1. This
- is LAN IRQ virtualization for deadlock avoidance. Under Windows 3.0
- and 3.0a, VIPX will not virtualize LAN IRQs. See section III.B.5
- VirtualizeIrq[0-F].
-
- The preferred version of Windows is a patched version of Windows 3.1
- or the Windows 3.11 update which contains this fix.
-
- Microsoft found a bug in the their Virtual Timer Device (VTD) driver
- which would cause a deadlock with the NetWare shell (NETX). This
- version of WIN386.EXE has a single byte patch which prevents the
- situation from occurring.
-
- The fix is in the Virtual Timer Device's control procedure. Without
- the patch (VTDA.386), a certain procedure is called during the Create_VM state.
- With the patch, this procedure is now called during the
- VM_Critical_Init state. The patch is applied at offset 441D8 in
- WIN386.EXE (544789 03-01-92 3:10a). The value at that location is 07
- (Create_VM). The value should be changed to 08 (VM_Critical_Init).
-
- The deadlock occurs as a result of the shell waiting for the timer
- tick to advance. When an NCP is sent, the shell will start it's
- retry time-out countdown. This is essentially a loop waiting for the
- timer tick value to advance to a certain point. The deadlock occurs
- when the tick value never increments and thus the shell is left in an
- infinite loop. The reason the tick count is never incremented is
- that the timer interrupt is not allowed to be simulated into V86 mode
- under certain circumstances. The patch to the VTD allows the timer
- interrupts to be simulated.
-
-
- II.B. Version Compatibility with Dedicated IPX
- -----------------------------------------------
-
- Novell officially ceased maintenance on the dedicate IPX driver
- (IPX.OBJ) version 3.10 dated 11-21-91. The last version of VIPX to
- explicitly support that driver is 1.10. The current version of VIPX
- supports IPXODI.COM only. It is strongly recommended that you update
- your dedicated IPX driver with IPXODI.COM when using versions of VIPX
- later than 1.10.
-
-
- II.C. Packet Size Limitations
- ------------------------------
-
- VIPX.386 will only virtualize packets which are 8000 bytes (7.81K) or
- less. Any DOS and Windows IPX or SPX applications which use networks
- with a physical packet size greater than 7.81K may not work with
- VIPX.386. For example, IBM Token Ring can be configured to run with
- 16K packets. A request by a DOS or Windows IPX application to send a
- 16K packet will be truncated to 7.81K. On the other hand, any 16K
- packet that is received by the LAN driver will be dropped because
- VIPX cannot allocate a packet big enough to handle it.
-
-
- II.D. Memory Manager Limitations
- ---------------------------------
-
- When an request is passed up from IPX, VIPX will immediately test the
- request buffer to see if it is in global memory already. If it is in
- global memory, the request will be passed back down to IPX without
- any virtualization. For example, a TSR loaded before Windows is
- considered to be in global memory. If that TSR calls IPX, VIPX will
- test the requests and pass them back down to IPX. This is done
- because there is no need to virtualize requests which are already
- global.
-
- The use of UMB memory complicates the test for global memory. There
- are two basic scenarios. Scenario One is when a TSR has been loaded
- high before Windows was loaded. In this case, IPX requests will come
- from global UMB memory. VIPX will simply pass these requests back
- down to IPX. Scenario Two is when a TSR is loaded high in a Windows
- DOSBOX. In this case, IPX requests will come from local UMB memory.
- VIPX will virtualize these requests.
-
- Using HIMEM.SYS and EMM386.SYS, all tests for global UMB memory will
- work properly under both of scenarios.
-
- However, there are some memory managers that do not work properly
- under Windows. With these drivers, all LOCAL DOSBOX UMB's look as if
- they are GLOBAL UMB's. Under Scenario Two, when a TSR calls IPX, VIPX
- will test the request buffer and think that it is in global UMB
- memory when it is really in LOCAL UMB memory. As a result, VIPX will
- pass a local ECB to IPX without virtualization. The normal result of
- this is a hung machine or data corruption.
-
- This problem has been reported with Compaq's EXMEM.SYS (version
- unknown)and QEMM using STEALTH mode.
-
- If you are using one of these two memory managers, all TSRs USING THE IPX
- INTERFACE (INCLUDING IPX ITSELF) MUST BE LOADED IN CONVENTIONAL
- MEMORY.
-
-
- III. System Settings in the SYSTEM.INI File
- --------------------------------------------
-
- III.A. [386Enh] Section
- ------------------------
-
- III.A.1. TimerCriticalSection
- ------------------------------
-
- As of version 1.15 of VIPX, TimerCriticalSection is required to be set
- on. The recommended setting is as follows:
-
- [386Enh]
- TimerCriticalSection=10000
-
- The reason for this parameter is to avoid a deadlock with the LAN IRQ
- Virtualization code. See section III.B.5 VirtualizeIrq[0-F].
-
-
- III.B. [VIPX] Section
- ------------------------
-
- Under most circumstances, VIPX will work fine under the default
- configuration. However, there may be some applications which require
- custom configuration of the driver. This is a list of SYSTEM.INI
- parameters which can be used to configure VIPX:
-
- [VIPX]
- VipxMappingPages =[number of 4K pages] (default = 16)
- VipxFailOverSizedPackets =[ON|OFF|TRUE|FALSE] (default = OFF)
- VpicdFix =[ON|OFF|TRUE|FALSE] (default = ON)
- AutoIrqVirtualize =[ON|OFF|TRUE|FALSE] (default = ON)
- VirtualizeIrq[0-F] =[ON|OFF|TRUE|FALSE] (default = OFF)
- KeepLongLivedSocketsOpen =[ON|OFF|TRUE|FALSE] (default = OFF)
-
- (BELOW ARE CONFIGURATION PARAMETERS FOR BETA VERSIONS ONLY!)
- VipxErrorMessages =[ON|OFF|TRUE|FALSE] (default = OFF)
- VipxWarningMessages =[ON|OFF|TRUE|FALSE] (default = OFF)
- VipxBreakOnErrors =[ON|OFF|TRUE|FALSE] (default = OFF)
- VipxBreakOnWarnings =[ON|OFF|TRUE|FALSE] (default = OFF)
- VipxOutDebugStrOnErrors =[ON|OFF|TRUE|FALSE] (default = OFF)
- VipxOutDebugStrOnWarnings =[ON|OFF|TRUE|FALSE] (default = OFF)
-
-
- III.B.1. VipxMappingPages
- --------------------------
-
- This is the number of pages that VIPX can use to globalize requests
- to the global IPXODI.COM driver. VIPX is not absolutely guaranteed
- to have all of these pages available at any one point, since this is
- the requested number of pages for SHARED global mapping which VIPX
- makes to the Windows VMM at initialization time.
-
-
- III.B.2. VipxFailOverSizedPackets
- ----------------------------------
-
- This parameter tells VIPX to fail any requests which require more
- than the maximum allowed globalization size. The actual maximum will
- vary according to which media the user is on. The absolute maximum
- is 8000 (decimal) bytes. With media that have smaller packets than
- 8000 bytes, the maximum allowed size is the maximum packet size that
- can be put onto the media.
-
-
- III.B.3. VpicdFix
- ------------------
- III.B.4. AutoIrqVirtualize
- ---------------------------
-
- VpicdFix and AutoIrqVirtualize are synonyms for the same option.
- The VPICD VXD has a problem of allowing an interrupt to be simulated
- to a VM which previously held the Windows Critical Section but currently
- does not. (For more details, see III.B.5. VirtualizeIrq[0-F]). This
- causes a deadlock situation when LAN I/O is attempted in another VM.
- To get around the problem, VIPX will take over the job of virtualizing
- LAN IRQs and will simulate LAN interrupts only to the owner of the
- Windows Critical Section.
-
- This option is used to turn on or off the automatic detection and
- virtualization of LAN IRQs when ODI LAN drivers or dedicated LAN
- drivers are loaded. The reason for this option is to allow OEMs to
- virtualize their LAN drivers with their own VXDS.
-
-
-
- III.B.5. VirtualizeIrq[0-F]
- ----------------------------
-
- This is option is automatically configured excepted on machines using
- the IBM LAN Support Program. This parameter tells VIPX to virtualize
- the specified (hex) Interrupt request (IRQ) line. This feature is
- mainly designed to assist the IBM LAN Support Program in avoiding the
- "Black Screen of Death".
-
- Because of a design flaw in the VPICD.386 driver, network interrupts
- will sometimes deadlock a PC running Windows 3.X. VIPX 1.15 attempts
- to avoid the deadlock by virtualizing the Network Interface Card's
- (NIC) IRQ(s). With ODI and dedicated IPX (IPX.OBJ) drivers, VIPX
- will automatically read the configuration of the NIC from the driver
- and virtualize the selected IRQs.
-
- The only time that VIPX cannot automatically detect the LAN IRQs is
- when using the IBM LAN Support Program with either SLANSUP.OBJ or
- LANSUP.COM. In this case, the LAN IRQ is not readable from the
- driver. The reason (so I am told) is that IBM LAN Support does not
- have an API to read the IRQ setting on the NIC: The only way to get
- this information is to read the NIC hardware itself. The problem
- with doing this is that the hardware can be Token Ring, PCN2 or
- Ethernet. This means that VIPX would have to be aware of all of the
- different configurations of these types of hardware. Instead of this,
- VIPX requires the IBM LAN Support user to specify the NIC IRQ in the
- [VIPX] section of the SYSTEM.INI. IRQs range from 0 to F (hex). An
- example is listed below:
-
- [VIPX]
- VirtualizeIrq2=TRUE
- VirtualizeIrq3=TRUE
-
- In this example, VIPX will virtualize both IRQ 2 and IRQ 3. VIPX can
- virtualize up to 4 different LAN IRQs. The reason for virtualizing
- multiple IRQs is to allow other LAN cards and protocols to be
- installed on the same PC and prevent them from deadlocking the
- machine. For example, you may have IPX running through an NE2000
- card on IRQ 3 and TCP/IP running through to an IBM Token Ring card on
- IRQ 2.
-
- One side note. On an AT class PC, IRQ 2 is really IRQ 9 on the
- physical machine. IRQ 2 is a special cascade interrupt used to
- access IRQs 9-F. Whenever a NIC raises the IRQ 2 line, the hardware
- configuration on the PC's bus actually issues an IRQ 9. Also, if a
- NIC is configured to IRQ 2, the LAN driver during its initialization
- procedure will detect that the PC is an AT class machine and will
- correctly configure its software to use IRQ 9.
-
- Getting to the point, the Windows VPICD VxD will not allow IRQ 2 to
- be exclusively virtualized by any VxD. However, when VIPX detects
- that IRQ 2 is to be virtualized, it changes the IRQ to 9. So if you
- are using the IBM LAN Support Program on an IBM Token Ring NIC set to
- IRQ 2, you should have not trouble. The following two configurations
- are equivalent:
-
- [VIPX]
- VirtualizeIrq2=TRUE
-
- or
-
- [VIPX]
- VirtualizeIrq9=TRUE
-
- One important note when using VIPX 1.15 for higher: Set the
- TimerCriticalSection under the [386Enh] section in the SYSTEM.INI as
- follows:
-
- [386Enh]
- TimerCriticalSection=10000
-
- The reason for this is that the timer interrupt can cause a deadlock
- with the virtualization of the LAN interrupt.
-
- You should note that the VirtualizeIrq[0-F] can be used to override
- any auto-virtualization of LAN IRQs. For example, if your ODI LAN
- driver is configured for IRQ 5, VIPX will automatically detect that
- IRQ 5 is to be virtualized. However, if you have
-
- [VIPX]
- VirtualizeIrq5=FALSE
-
- in your SYSTEM.INI, VIPX will override the automatic detection of IRQ
- 5 and not virtualize the interrupt.
-
- Another way to disable the auto-virtualization feature of VIPX is
- with the VpicdFix or AutoIrqVirtualize parameters (See III.B.3.
- VpicdFix or III.B.4 AutoIrqVirtualize).
-
-
- III.B.6. KeepLongLivedSocketsOpen
- ----------------------------------
-
- This options prevents VIPX from closing long-lived sockets which
- where opened while Windows was loaded when exiting Windows. This
- only applies to sockets which were opened from a DOS application or
- TSR: Long-lived sockets opened from a Windows application will be
- closed when Windows exits. The problem with keeping long-lived
- sockets opened is that some IPX applications leave their sockets open
- before exiting. VIPX by default will close all sockets opened during
- Windows. However, there are some globally loaded TSRs (i.e. loaded
- before Windows) which need to open and close sockets during a Windows
- session. When exiting Windows, VIPX will close these sockets. For
- TSRs which need to open sockets while Windows is loaded, use the
- following in the SYSTEM.INI:
-
- [VIPX]
- KeepLongLivedSocketsOpen=TRUE
-
-
-
- III.B.7. VipxErrorMessages
- ---------------------------
- III.B.8. VipxWarningMessages
- -----------------------------
- III.B.9. VipxOutDebugStrOnErrors
- ---------------------------------
- III.B.10. VipxOutDebugStrOnWarnings
- -----------------------------------
- III.B.11. VipxBreakOnErrors
- ---------------------------
- III.B.12. VipxBreakOnWarnings
- -----------------------------
-
- These parameters are only enabled in Beta versions of VIPX. With Beta
- software, the user can enable/disable these parameters to make VIPX
- print error/warning messages to the user screen and debugger screen.
- They can also instruct VIPX to execute a break point whenever an
- error/warning occurs so that debugging can be done on site.
-
-
- IV. Programming to the VIPX Interface
- --------------------------------------
-
- IV.A. Getting the VIPX API Entry Point
- ---------------------------------------
-
- VIPX supports API calls from 16-bit DOS and Windows applications.
- This is the interface which NWIPXSPX.DLL uses to issue calls to VIPX.
-
- To get the VIPX Device V86/PM API Entry Point, a 16-Bit DOS/Windows
- application must do the following:
-
- DWORD VIPX_API_ADDR;
- #define WIN_GET_DEV_API_ENTRY_PT 0x1684
- #define VIPX_DEVICE_ID 0x0200
- GetVipxApiAddr ()
- {
- _asm
- {
- mov ax, WIN_GET_DEV_API_ENTRY_PT
- mov bx, VIPX_DEVICE_ID
- int 2fh
- mov word ptr VIPX_API_ADDR+2, ES
- mov word ptr VIPX_API_ADDR, DI
- }
- if (!VIPX_API_ADDR)
- {
- print error - NO PM API! RIGHT VERS OF VIPX?
- handle error
- }
- }
-
- Once the API address is obtained, an application then can make a far
- call to the VIPX API Handler to do two primary functions:
-
- 1. Request an 16-Bit PM IPX/SPX operation (SendPacket, etc...).
- 2. Request a device specific operation (GetVersion, GetDiagnotics).
-
- The way that VIPX differentiates between the two types of APIs is by
- the value in the BH register. If BH equals 00h, then VIPX will
- handle a 16-Bit Protected Mode IPX/SPX operation. NOTE: The V86
- IPX/SPX API interface (i.e. DOS application) is done through the
- globally loaded IPX TSR itself. This way, DOS applications do not
- need to change the way they access the IPX/SPX APIs under Windows and
- DOS.
-
- If BH equals the character 'V' (56h), then VIPX will perform a
- V86/PM Device Specific API. These APIs are listed below (and will be
- presented in full specification later):
-
- GetVipxVersion
- GetVipxDiagnostics
-
- For IPX/SPX operations (BH = 00h), a 16-bit Windows program must
- follow the IPX/SPX API specification in the NetWare System Calls-DOS
- manual under the Communications chapter. Coverage of the DOS IPX/SPX
- APIs is beyond the scope of this document. However, an example is
- provided below for reference:
-
- DWORD VIPX_API_ADDR;
- int ccode;
- CallPmIpxSpxApi ()
- {
- _asm
- {
- mov bx, (IPX operation number)
- (set other registers as specified by the function)
- call VIPX_API_ADDR
- (save return registers as specified by the function)
- ErrorCondition = (function error condition)
- }
- if (ErrorCondition)
- {
- print error - function failed!
- handle error
- }
- }
-
-
- IV.B. Memory Management Issues
- -------------------------------
-
- One problem with using the IPX/SPX interface in Windows applications
- is that all memory used by VIPX must be fixed. Windows really has two levels of
- memory
- management: high-level segment memory management used by Windows
- applications and low-level kernel page memory management. The
- high-level memory manager (referred in this document as GUI MM) allocates pages
- from the
- low-level memory manager (referred in this document as Page MM) and creates what
- is
- known as memory segments. A segment can consist of several pages.
- The pages allocated by the GUI MM from the Page MM are totally under
- the control of GUI MM. The GUI MM can decide if certain pages of
- segments are to be paged out, discarded, etc. The Page MM, on the
- other hand, just carries out the orders of the GUI MM: it can never
- tell the GUI MM what to do with pages which it owns. For example,
- the Page MM can never tell the GUI MM that it locked a page belonging
- to a segment.
-
- With this last example in mind, let's discuss VIPX. Since VIPX is an
- asynchronous driver, all application ECBs, fragments and ESRs must be
- in locked memory to insure that they can be accessed during interrupt
- time. If an ECB, fragment or ESR is in moveable memory, it could
- move when VIPX "owns" that memory. For example, assume an ECB is
- located at 16-bit PM address 0A10:0000 in moveable memory. Assume
- this address translates to 32-bit linear address 8140ACB0. Assume
- also that the application gives the ECB to VIPX and VIPX commences to
- work on it. Now if Windows does garbage collection via
- GlobalCompact(), the linear mapping for the 16-bit address 0A10:0000
- may change from 8140ABC0 to 81408100. When VIPX tries to access the
- old linear address 8140ABC0, memory corruption will occur because
- VIPX is accessing the wrong memory. VIPX does call the Page MM to
- lock all pages belonging to ECBs and fragments passed to it.
- However, this in no way tells the GUI MM that pages were locked. The
- GUI MM can simply do a GlobalCompact() and remap those pages to
- different linear addresses! In order for the GUI MM to know that a
- GlobalCompact() is not supposed to remap a segment (and thus the
- pages in that segment) the Windows application or DLL must call the
- GUI MM's GlobalFix() function: VxDs (such as VIPX) cannot call
- 16-bit GUI MM functions.
-
- The reason why Windows applications must lock the memory
- instead of the NWIPXSPX.DLL was a decision based on performance. The
- writer of the DLL felt that it was better for applications to lock
- and unlock their own memory because they have a better idea of when
- that memory will be used intensively for network traffic. For
- example, if NWIPXSPX.DLL were to do a GlobalFix() on each ECB and
- fragments when it was submitted and then did a GlobalUnfix() when it
- was completed, there would be a lot of overhead when that memory used
- over and over again for network operations. If the application were
- to lock the memory, do 1000s of network operations and then unlock
- the memory, there would be much less overhead.
-
- To avoid memory corruption, follow the guidelines below:
-
- 1. Read Chapter 15 (Memory Management) and 16 (More Memory
- Management) of the MS Windows SDK Version 3.1. These will explain
- many terms and concepts which will be discussed below.
-
- 2. Read Chapter 2 (Windows Memory Management) of Windows Internals
- by Matt Pietrek, ISBN 0-201-62217-3.
-
- 3. Define all ESRs, routines called by ESRs and data accessed by
- ESRs in one module. In the .DEF file for your project, define the
- module's code and data segments as FIXED.
-
- 4. Use GlobalFix() on all dynamically allocated heap memory
- (via GlobalAlloc() or LocalAlloc()) which is to be used by VIPX or an
- ESR. GlobalFix() informs the GUI kernel memory manager to fix
- the linear base of the selector until a GlobalUnfix() is done on
- the memory.
-
- 5. When using GlobalAlloc(), use the GMEM_DDESHARE and GMEM_FIXED
- options. GMEM_DDESHARE will allocate the memory, according to sources, high
- up on the global heap. This will avoid using conventional memory and
- will allow room for other Windows applications to load after your
- application has been loaded and initialized. The GMEM_FIXED option
- tells the GUI kernel memory manager that the memory is FIXED. This
- does not mean that the linear address will remain unchanged, but it
- does mean that the segmented address (selector:offset) will not
- change. After the call to GlobalAlloc(), call GlobalLock() to
- dereference the global memory handle. This provides a far pointer
- (selector:offset) to the memory which will not change. Next call
- GlobalFix() to fix the linear base of the selector so that the
- 32-bit linear mapping of the selector:offset does not change.
-
- 6. If local moveable or discardable code or data segments are used
- for ECBs, fragments or ESRs, use GlobalFix() on those segments.
-
- 7. After you load your application and then you get a message from
- Windows that there are not enough resources to load another program,
- try changing the attributes of your segments. I believe this
- condition occurs because too much conventional memory has been
- allocated to fixed memory objects and Windows needs to allocate
- conventional memory to load a program. One strategy may be to do a
- GlobalCompact() with a argument of -1 at initialization time. This
- will move all moveable segments and discard all discardable
- segments. Next call GlobalAlloc() for one large moveable global
- object which contains all of your ECBs, fragments and data which must
- be accessed from VIPX or your ESRs. Next call GlobalLock() to
- dereference the global handle. Last call GlobalFix() to fix the
- linear address of the object. (*This last step is untested so there
- may be some problems with it). Also make your ESR code as small as
- possible using PostMessage to indicate to your application that an
- ECB has completed. This way, your FIXED code segment will have
- little impact on conventional memory.
-
- 8. Read Chapter 4 of The Windows Programmer's Guide to DLLs and
- Memory Management by Mike Klein, ISBN 0-672-30236-5.
-
- Previously we had recommended that you use GlobalPageLock() and
- GlobalPageUnlock() on the ECBs and fragment you needed locked. We
- now recommend GlobalFix() and GlobalUnfix() because these calls do
- not cause the memory to move to conventional memory, but still provides
- the functionality of page locking that is needed for VIPX to touch
- the memory in the context of other VMs.
-
-
- IV.C. APIs Specific to VIPX.386
- --------------------------------
-
- There are two APIs via the VIPX API entry point which are specific
- to VIPX.386 only. These are:
-
- GetVipxVersion
- GetVipxDiagnostics
-
- The device specific APIs are called in the same manner as the IPX/SPX
- APIs except that BH = 56h. Some sample code is listed below:
-
- DWORD VIPX_API_ADDR;
- #define VIPX_DEVICE_API_TAG 'V'
- int ccode;
- CallVipxDeviceSpecificApi ()
- {
- _asm
- {
- mov bh, VIPX_DEVICE_API_TAG
- mov bl, api_function_number
- (set other registers for API function)
- call VIPX_API_ADDR
- mov ccode, ax
- }
- if (!ccode)
- {
- print error - api function failed!
- handle error
- }
- }
-
- The full API specification for GetVipxVersion is as follows:
-
- GetVIPXVersion
-
- This procedure puts the VIPX major and minor version numbers into
- the client AH and AL.
-
- To call this API a DOSBOX or Windows 3.0 application
- must first get the VIPX Device API entry point (see
- VIPX_V86_API_Handler or VIPX_PM_Handler). Then
- it must do the following:
-
- Example:
-
- DWORD VIPX_API_ADDR;
- BYTE VIPX_Major_Version;
- BYTE VIPX_Minor_Version;
- #define VIPX_DEVICE_API_TAG 'V'
- #define VIPX_GET_VERSION 0x00
- int ccode;
- main ()
- {
- _asm
- {
- mov bh, VIPX_DEVICE_API_TAG
- mov bl, VIPX_GET_VERSION
- call VIPX_API_ADDR
- mov ccode, ax
- mov VIPX_Major_Version, bh
- mov VIPX_Minor_Version, bl
- }
- if (!ccode)
- {
- print error - api function failed!
- handle error
- }
- }
-
- Entry:
- BH 056h - VIPX Device API
- BL 0 - Get VIPX Version function
-
- Exit:
- BH VIPX Major Version
- BL VIPX Minor Version
- AX Return code
- 0000h - success
-
- Uses:
- AX, BX, Flags
-
- The full API specification for GetVIPXDiagnostics is as follows:
-
- GetVIPXDiagnostics
-
- This procedure puts the diagnostics for VIPX into the
- specified client diagnostic buffer. The buffer is 112 bytes
- and has the following structure:
-
- VIPXDiagStruc STRUC
- MallocCount dd ?
- MallocErrorCount dd ?
- FreeCount dd ?
- AllocPageCount dd ?
- FreePageCount dd ?
- PMRequestCount dd ?
- TSRServiceCount dd ?
- ServiceAESEventCount dd ?
- ServiceIPXEventCount dd ?
- PostEventCount dd ?
- IPXGetECBCount dd ?
- IPXGetECBBadPacketCount dd ?
- IPXGetECBOutOfResource dd ?
- IPXCountECBCount dd ?
- IPXReturnECBCount dd ?
- IPXRequestCount dd ?
- FreeECBCount dd ?
- AllocECBCount dd ?
- LockECBCount dd ?
- UnlockECBCount dd ?
- OpenSocketCount dd ?
- CloseSocketCount dd ?
- SendPacketCount dd ?
- ListenCount dd ?
- IPXScheduleEventCount dd ?
- AESScheduleEventCount dd ?
- CancelECBCount dd ?
- PMInt2FCount dd ?
- AllocPageErrorCount dd ?
- FreePageErrorCount dd ?
- FreeECBErrorCount dd ?
- MaxVipxAllocSize dd ?
- MaxVipxFragsSize dd ?
- TsrServiceReenterCnt dd ?
- IPXGetEcbSktNotOpenCnt dd ?
- IPXGetEcbNoEcbAvailCnt dd ?
- ENDS
-
- NOTE: Starting with VIPX 1.16, MaxVipxAllocSize, MaxVipxFragsSize, and
- TsrServiceReenterCnt have been updated to DWORDs. Also
- IPXGetEcbSktNotOpenCnt
- and IPXGetEcbNoEcbAvailCnt have been added.
-
-
- To call this API a DOSBOX or Windows 3.0 application
- must first get the VIPX Device API entry point (see
- VIPX_V86_API_Handler or VIPX_PM_Handler). Then
- it must do the following:
-
- Example:
-
- DWORD VIPX_API_ADDR;
- VIPXDiagStruc Diag;
- #define VIPX_DEVICE_API_TAG 'V'
- #define VIPX_GET_DIAGNOSTIC 0x01
- int ccode;
- main ()
- {
- _asm
- {
- mov bh, VIPX_DEVICE_API_TAG
- mov bl, VIPX_GET_DIAGNOSTIC
- mov ax,ds
- mov es,ax
- mov di, offset Diag
- mov cx, size VIPXDiagStruc
- call VIPX_API_ADDR
- mov ccode, ax
- }
- if (!ccode)
- {
- print error - api function failed!
- handle error
- }
- }
-
- Entry:
- BH 056h - VIPX Device API
- BL 1 - Get VIPX Diagnostics
- ES:SI ptr to client VIPX Diagnostics buffer.
- CX Length of diagnostics buffer in bytes
-
- Exit:
- ES:SI client's buffer now has VIPX diags.
- AX Return code
- 0000h - success
- 0FFFEh - diagnostics buffer too short
-
- Uses:
- AX, SI, ES, Flags
-
-
- IV.D. Handling Reentrancy Issues
- ---------------------------------
-
- A Windows application which uses the IPX/SPX APIs via VIPX must
- handle reentrancy issues. Since the IPX/SPX API is interrupt driven,
- any code running while interrupts are enabled can (and will) be
- interrupted at any time by IPX/SPX. When an IPX event completes, an
- ESR for that event is called. The ESR is entered with interrupts
- disabled. If that ESR reenables interrupts, then it can be
- reentered. This is usually a problem if:
-
- 1. The application has limited stack space
- 2. The application's ESR uses global variables
-
- An ESR must protect itself from these two reentrancy issues if it
- reenables interrupts. Interrupts can be reenabled by several
- different places. One of the most notorious is the PostMessage()
- Windows call. Any ESR using PostMessage() must be reentrant.
- Another notorious enabler of interrupts is IPXSendPacket.
- IPXSendPacket will call the LSL to transmit a packet. The LSL,
- however, will reenable interrupts during transmission of a packet.
- This can cause reentrancy into any ESR that calls IPXSendPacket.
-
- An ESR can handle reentrancy issues by
-
- 1. Switching to a stack exclusively for the ESR to use.
- 2. Provide a reentrancy queue.
-
- The stack switching is usually done in assembly. It is by far the
- easiest and most efficient way to protect your stack. The code for
- stack switching is listed below:
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~~~
- memL equ 1 ; large model
- ?PLM = 1 ; pascal conventions
- ?WIN = 1 ; epilogue/prolog code
-
-
- INCLUDE cmacros.inc
-
- sBegin DATA
- assumes DS, DATA
-
- ReenterCnt db 0
-
- OldSs dw 0
- OldSp dw 0
-
- EsrStack db 4096 dup (0)
-
- EndEsrStack label byte
-
- sEnd DATA
-
- sBegin CODE
- assumes CS, CODE
- ;
- ; This is the front-end procedure to the C function "ReceiveESR"
- ;
-
- externFP cESR
- PUBLIC aESR
- aESR proc FAR
-
- nop
- nop
- nop
- nop
-
- ; Don't know why Windows needs to inc bp
- inc bp
- push bp
- mov bp, sp
-
- ; Save data segment
- push ds
-
- ; Fix to our data segmented
- mov ax, DGROUP
- mov ds, ax
-
- ; Update our reenter cnt
- inc ReenterCnt
-
- ; Do we need to switch stack to ESR stack?
- cmp ReenterCnt, 2 ;are we reentering?
- jae CallEsr ;y: call esr
- mov ax, ss
- cmp ax, DGROUP ;same stack seg?
- jne SwitchStack ;n: switch
- lea ax, EsrStack ;ax=offset of EsrStack
- cmp sp, ax ;below the esr stack?
- jb SwitchStack ;y: switch
- lea ax, EndEsrStack ;ax=offset of end of EsrStack
- cmp sp, ax ;above the esr stack
- jae SwitchStack ;y: switch
-
- ; Call the esr
- CallEsr:
- push es
- push si
- cCall cESR
- lea sp, [bp-2]
-
- ; Update reenter cnt
- dec ReenterCnt
-
- ; Switch stacks back?
- jz SwitchStackBack
-
- ; return to IPX
- EsrExit:
-
- ; restore ds and bp
- pop ds
- pop bp
- dec bp
-
- ret
-
- SwitchStack:
- mov ax, sp
- mov OldSp, ax
- mov ax, ss
- mov OldSs, ax
- lea ax, EndEsrStack
- mov sp, ax
- mov ax, ds
- mov ss, ax
- jmp CallEsr
-
- SwitchStackBack:
- mov ax, OldSp
- mov sp, ax
- mov ax, OldSs
- mov ss, ax
- jmp EsrExit
-
- aESR endp
-
- sEnd CODE
- end
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~~~
-
- The above code switched to a 4K stack before calling the main ESR
- code. Notice that there is a reentrancy count to determine if a stack
- switch should occur. This is used to prevent switching the stack
- again if the ESR is reentered.
-
- The main ESR code is written in C. An example is listed below:
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~~~
- void far pascal cESR(ECB far *esr_ecb)
- {
- static ECB FAR * EcbQueue;
- static ECB FAR * EcbQueueTail = (ECB FAR *)&EcbQueue;
- static WORD ReenterFlag = 0;
-
- // Check for reentrancy
- if (ReenterFlag)
- {
- // Enqueue the ECB
- EcbQueueTail->Link = (void far *)esr_ecb;
- EcbQueueTail = (ECB far *)esr_ecb;
- esr_ecb->Link = NULL;
- return;
- }
-
- // Set the reentrant flag to indicate that we can't allow
- // another thread through the ESR.
- ReenterFlag++;
-
- // Process the current ECB
- NextEcb:
-
- // Do some work here if need be -- BUT BE QUICK!
- ...
-
- // Post a message to the Windows App that the event happened
- PostMessage (
- hMainWnd,
- USER_DEFINED_IPX_EVENT_HAPPPENED,
- 0,
- (DWORD) esr_ecb);
-
- // Check for anything on the queue
- CheckQueue:
- if (EcbQueue)
- {
- esr_ecb = EcbQueue;
- EcbQueue = (ECB FAR *)(EcbQueue->Link);
- if (!EcbQueue)
- EcbQueueTail = (ECB FAR *)&EcbQueue;
- goto NextEcb;
- }
-
- // Reset the reentrancy flag to indicate that the
- // first thread is done
- ReenterFlag--;
- }
-
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~~~
-
- One side note about the code above. PostMessage() will queue a
- message onto an applications message queue. This queue is limited in
- size. If the application is fairly LAN I/O intensive, the message
- queue can be overrun. In this case, PostMessage() will simply drop
- the message. If this happens, your application will never get
- notified of the event. To insure that messages are never dropped,
- you may want to increase the size of your message queue. Another
- alternative is to have the message indicate that an event happened
- and the message loop will then poll all ECBs to determine which ones
- completed. This way, if a message is dropped, it won't matter: the
- main message loop will still detect that the ECB completed and will
- be able to reuse the ECB.
-
-
-
- IV.E. Using NWIPXSPX.DLL
- -------------------------
-
- Most Windows applications will use the IPX/SPX APIs via the
- NWIPXSPX.DLL. The reason is that NWIPXSPX.DLL address the issues for
- both Standard and Enhanced mode Windows. Under Standard mode,
- NWIPXSPX will use TBMI2.COM to virtualize the local ECBs passed to
- it. Under Enhanced mode, NWIPXSPX.DLL will use VIPX.386 to
- virtualize ECBs. If you Windows application is targeted toward
- Enhanced mode only, then you may find it more convenient to program
- directly to the VIPX interface in assembly. However, if you are
- interested in using an established C interface for IPX/SPX, then
- NWIPXSPX.DLL is the way to go. There is example code using NWIPXSPX.DLL
- on the CompuServe NOVDEV forum.
-
-
-
- IV.F. Windows Application Resource Cleanup
- -------------------------------------------
-
- Under DOS, many IPX applications took advantage of the so called
- "short-lived" or "lazy-man's" sockets. These are sockets which
- automatically closed when the DOS application terminates. These
- sockets were associated with a specific PSP or DOS task. When
- a short-lived socket was opened, the current DOS task was recorded for
- that socket. When a DOS Terminate system call was done, IPX would
- match the PSP of the terminating application with the sockets which
- are associated with that PSP and close them. This all worked great
- under DOS.
-
- However, Enhanced Windows 3.X came along and totally changed the
- rules. DOS applications still run the same way they used to, except
- now they run in separate VMs. IPX and VIPX have no trouble keeping
- track of them. With Windows applications, this is a different story
- all together. Windows applications are different tasks that run in
- VM1 (the system VM). Windows applications use 16-bit selectors which
- define dynamically paged segments in memory. When a segment is
- allocated to a Windows application, it can be paged, moved and
- discarded, depending on the attributes and state of the arena that
- defines the segment. When a Windows application terminates, the
- segment selectors allocated to the application are freed.
-
- The problem is that by the time VIPX is able to get an indication
- that the task is terminating, the selectors have been freed.
- Touching the memory that used to belong to those selectors could
- cause a GPI in ring-0. This is what happens:
-
- When an ECB is submitted to VIPX, it is in 16-bit selector:offset
- format. VIPX immediately converts that to a 32-bit flat, linear
- address. It then links the ECB onto a list using the Next field in
- the ECB. Let's say, for example, we have 3 ECBs: ECB1, ECB2, ECB3.
- ECB1 and ECB2 are submitted and are queued onto some sort of list.
- The list would look like this:
-
- ListHeadPtr -> ECB1 -> ECB2 <- ListTailPtr
-
- Now an Windows application submits ECB3. VIPX converts the
- 16-bit selector:offset pointer to ECB3 into a linear address, let's
- call it LINEAR3. Now VIPX puts it on the list with the following
- operations:
-
- ; ASSUME EAX has the value of LINEAR3
- mov esi, ListTailPtr ; get -> to last ECB on list (in this case ECB2)
- mov [esi].Next, eax ; ECB2.Next = LINEAR3
- mov [eax].Next, 0 ; ECB3.Next = NULL
- mov ListTailPtr, eax ; Tail = LINEAR3
-
- and our list look like:
-
- ListHeadPtr -> ECB1 -> ECB2 -> ECB3 <- ListTailPtr
-
- Now if the application goes away, the selector have just
- become invalid and thus LINEAR3 is bogus. So when VIPX traverses the
- list, it will hit the invalid linear address. For example:
-
- mov esi, ListHeadPtr ; esi -> ECB1
- mov esi, [esi].Next ; esi -> ECB2
- mov esi, [esi].Next ; esi = LINEAR3 = BOGUS
- ; (no GPI yet--just reading a
- ; value from memory and placing
- ; it into a register)
- mov esi, [esi].Next ; GPI!!!!!! Black screen with
- ; cursor at upper left-hand corner.
-
- The point is: There is absolutely no way for VIPX to avoid this.
- VIPX (a VxD) does not get informed when a task in VM1 (i.e. a Windows
- application) terminates: VxDs are only notified when a VM
- terminates. (NOTE: There are the Begin_PM_App and End_PM_App
- callbacks to VxDs, but they are not for Windows applications. They
- indicate when the protected mode kernel load and unload--i.e. when
- Windows begin and exits). If there was an terminate task indication
- to VxDs, VIPX could clean up resources for that task. But Windows system level APIs
- requires the application to worry about cleaning up after itself. Therefore, the
- application must clean
- up by closing the socket. If resources are left dangling while VIPX owns them, then a
- crash will occur.
-
- One suggestion is for VIPX to use the Intel 80386 lsl instruction to check if a segment
- is present in memory before touching it. The problem is that this won't work with the
- above described architecture
- of VIPX. VIPX translates the selector:offset to a linear address
- when the ECB is submitted. The ECB is put on the list and the
- linear address is used to fetch it from the list. VIPX will never
- access the ECB via the 16-bit selector:offset address provided by the
- Windows application.
-
- The suggestion is that Windows applications close all sockets belonging to
- them before exiting. In addition, Novell suggest that all listens posted on
- the sockets be canceled before the socket is closed.
-
- IV.G. Using GlobalDOSAlloc()
- -----------------------------
-
- Interesting news about GlobalDOSAlloc() in Windows found in chapter 2
- of Windows Internals by Matt Pietrek (ISBN 0-201-62217-3). On page
- 153, GlobalDOSAlloc() is described as a wrapper routine around
- GlobalAlloc(). However, the memory is allocated via the GA_ALLOC_DOS
- flag. This looks like it allocated FIXED memory from the global
- heap. A routine called GSearch() will eventually get called and
- tests if that FIXED memory is below 1 MB. If not, it fails,
- otherwise it succeeds. There appears to be no GlobalFix()
- associated with that memory, so if you do a GlobalFix()
- on all memory allocated via GlobalDOSAlloc(). They reason is that
- the page(s) for that memory may be switched to a different linear
- address during garbage collection. If so, then a GPI will occur--if
- not right away, then eventually.
-
-
-
- V. Global DOS TSRs using IPX/SPX
- ---------------------------------
-
- TSRs loaded before Windows can call IPX without the intervention of
- VIPX. This is done by ORing the BX register with 8000h (i.e. setting
- the high-bit of the function number). This is a previously
- undocumented feature in IPX to support the globally loaded NETX
- driver. When IPX sees that a request has the high-bit set, it will
- assume that the request came from a globally loaded TSR and will not
- forward the request to VIPX.
-
- The only problem with the high-bit is that SPX does not recognize
- it. When SPX gets a request with a high-bit, SPX ignores it and
- reposts the ECB to IPX without the preserving the high-bit. VIPX
- will then get the IPX request and then determine if the ECB is really
- loaded in global memory. If so, then it will simply pass the global
- request back down to IPX with the high-bit set.
-
-