home *** CD-ROM | disk | FTP | other *** search
-
- EXAMINING OS/2 DEVICE DRIVERS WITH "WINDOWS SOURCE"
- ---------------------------------------------------
-
- As its name suggests, "Windows Source" is primarily intended for use with
- Windows executables. However, many OS/2 executables use the same
- segmented-executable (or "new" executable, or NE) file format as Windows,
- so Windows Source can be extremely helpful with OS/2. In particular,
- OS/2 device drivers, for both OS/2 1.x and 2.x, use the NE format.
-
- Windows Source has always worked with OS/2 device drivers, but now
- (starting with WINP version 1.16, and EXEDUMP version 1.36) it will
- provide you with more information. To examine an OS/2 device driver, you
- should use the -OS2DD switch. For example:
-
- C:\OS2\DRIVERS>winp -os2dd driver.sys
-
- In this example, WINP will produce a driver.wdf file that you then pass
- to Sourcer itself (both WINP and SR should be on the path):
-
- C:\OS2\DRIVERS>sr driver.wdf
-
- Sourcer will produce a driver.lst file containing the assembly-language
- source for driver.sys.
-
- Note that some OS/2 *.SYS files do not use the NE format: in general,
- files with the name V*.SYS are linear executables that use the LX format,
- not currently supported by Windows Source (let us know if you want LX
- support: if enough people ask for it, we'll do it!). Some *.SYS files are
- just DOS device drivers that can be examined with Sourcer without WINP.
- Finally, some (such as PMDD.SYS) are Presentation Manager device drivers.
- These are NE files that can be examined with Windows Source, without
- using the -OS2DD switch.
-
- In the .LST file, Sourcer will identify the Strategy function and, if
- present, the IDC (inter-device communications) function. If there are
- multiple device drivers in the same .SYS file, each Strategy function
- will be labelled Strategy_1, Strategy_2, and so on. Sourcer will also
- identify the fields in the device-driver header(s).
-
-
- Locating DevHlp
- ---------------
-
- After quickly glancing through the .LST file, the first thing you should
- do is identify the DevHlp entry point (the address of the device helper
- function provided by OS/2). Unfortunately, Sourcer can't do this
- automatically. However, it is easy to find: look through the code for
- calls through a data item, where the DL register is being set with
- something that looks like a function number. For example:
-
- 2.0067 mov bl,data_0014 ; (1.003A=0)
- 2.006B xor bh,bh
- 2.006D mov dl,1Ch
- 2.006F call dword ptr data_0006 ; (1.0028=0)
-
- The data item being referenced (data_0006 in this example) will almost
- definitely be DevHlp. To double-check that it is, you need to find where
- it's being initialized. You should find something like this, somewhere
- in the .LST file:
-
- les bx,dword ptr [bp+4]
- mov ax,es:[bx+0Eh]
- mov word ptr data_0006,ax
-
- The DevHlp entry point is passed to an OS/2 device driver in offset 0Eh
- of an INIT request packet, and the device driver is expected to save this
- away somewhere. (We'll discuss finding the function that handles INIT
- and other request packets below.)
-
- Having convinced yourself that the data item is the DevHlp entry point,
- you should search for all occurrences of this item, and replace them with
- the string "DevHlp." In our example, all cases of "data_0006" would be
- replaced with "DevHlp."
-
- Once you can see the DevHlp calls, you can further clarify the code using
- the function numbers in DL. Here is a list of some DevHlp calls:
-
- DL Function name
- --- -------------
- 00h SchedClockAddr
- 01h DevDone
- 02h Yield
- 03h TCYield
- 04h Block
- 05h Run
- 06h SemRequest
- 07h SemClear
- 08h SemHandle
- 09h PushReqPacket
- 0Ah PullReqPacket
- 0Bh PullParticular
- 0Ch SortReqPacket
- 0Dh AllocReqPacket
- 0Eh FreeReqPacket
- 0Fh QueueInit
- 10h QueueFlush
- 11h QueueWrite
- 12h QueueRead
- 13h Lock
- 14h Unlock
- 15h PhysToVirt
- 16h VirtToPhys
- 17h PhysToUVirt
- 18h AllocPhys
- 19h FreePhys
- 1Ah SetROMVector
- 1Bh SetIRQ
- 1Ch UnSetIRQ
- 1Dh SetTimer
- 1Eh ResetTimer
- 1Fh MonitorCreate
- 20h Register
- 21h DeRegister
- 22h MonWrite
- 23h MonFlush
- 24h GetDOSVar
- 25h SendEvent
- 26h ROMCritSection
- 27h VerifyAccess
- 28h Reserved
- 29h Reserved
- 2Ah AttachDD
- 2Bh InternalError
- 2Ch Reserved
- 2Dh AllocGDTSelector
- 2Eh PhysToGDTSelector
- 2Fh RealToProt
- 30h ProtToReal
- 31h EOI
- 32h UnPhysToVirt
- 33h TickCount
- 34h GetLIDEntry
- 35h FreeLIDEntry
- 36h ABIOSCall
- 37h ABIOSCommonEntry
- 38h GetDeviceBlock (?)
- 3Ah RegisterStackUsage (?)
- 3Ch VideoPause
- 3Dh DispMsg
- 50h RegisterPDD
- 51h RegisterBeep
- 52h Beep
- 53h FreeGDTSelector
- 54h PhysToGDTSel
- 55h VMLock
- 56h VMUnlock
- 57h VMAlloc
- 58h VMFree
- 59h VMProcessToGlobal
- 5Ah VMGlobalToProcess
- 5Bh VirtToLin
- 5Ch LinToGDTSelector
- 5Dh GetDescInfo
- 5Eh LinToPageList
- 5Fh PageListToLin
- 60h PageListToGDTSelector
- 61h RegisterTmrDD
- 63h AllocateCtxHook
- 64h FreeCtxHook
- 65h ArmCtxHook
- 66h VMSetMem
- 67h OpenEventSem
- 68h CloseEventSem
- 69h PostEventSem
- 6Ah ResetEventSem
- 6Ch DynamicAPI
-
- (Much of this list, like most of the information here, comes from
- Ray Duncan's excellent book for OS/2 1.x, "Advanced OS/2
- Programming", published by Microsoft Press in 1989; Chapters 17, and
- Part 3 of the Reference, are devoted to OS/2 device drivers; there
- are of course new functions added in OS/2 2.0, but Duncan's book
- provides the basic core of information you'll need. For information
- on the newer 2.0 functions, see Steven J. Mastrianni's book "Writing
- OS/2 2.0 Device Drivers in C", New York: Van Nostrand Reinhold (VNR),
- 1992, 407 pp., ISBN 0-442-01141-5.)
-
- As an example of how identifying the DevHlp entry point can help clarify
- code, look at the following function, where the data item "sub_0006" has
- already been replaced with "DevHlp":
-
- sub_0030 proc near
- 2.2E35 push bp
- 2.2E36 mov bp,sp
- 2.2E38 mov ax,[bp+4]
- 2.2E3B mov bh,[bp+6]
- 2.2E3E mov bl,[bp+8]
- 2.2E41 mov dl,13h
- 2.2E43 call dword ptr DevHlp ; (1.0028=0)
- 2.2E47 jnc short loc_0286
- 2.2E49 xor ax,ax
- 2.2E4B xor dx,dx
- 2.2E4D jmp short loc_0287 ; (2E53)
- 2.2E4F loc_0286: ; xref 2.2E47
- 2.2E4F mov dx,ax
- 2.2E51 mov ax,bx
- 2.2E53 loc_0287: ; xref 2.2E4D
- 2.2E53 pop bp
- 2.2E54 retn
- sub_0030 endp
-
- By itself, this piece of code doesn't make sense. However, from the list
- of function shown above, we know that DevHlp function DL=13h is Lock.
- Consulting Duncan's book (or another source of information on OS/2 device
- drivers), we find:
-
- Lock -- locks a memory segment so it won't be swapped or moved during
- I/O Call with:
-
- AX selector/segment
- BH duration (0=short term; 1=long term)
- BL wait/no-wait flag (0=wait until segment locked; 1=don't wait)
- DL 13h
- Returns (success):
- Carry clear
- AX:BX lock handle
- Returns (failure):
- Carry set
-
- Suddenly, this block of code makes sense. First, sub_0030 is nothing but
- a wrapper around the DevHlp call. We can now add signifigant information
- to sub_0030 as follows:
-
- DevHlp_Lock proc near
- 2.2E35 push bp
- 2.2E36 mov bp,sp
- 2.2E38 mov ax,[bp+4] ;; selector/segment
- 2.2E3B mov bh,[bp+6] ;; duration
- 2.2E3E mov bl,[bp+8] ;; wait/no-wait
- 2.2E41 mov dl,13h ;; Lock
- 2.2E43 call dword ptr DevHlp ; (1.0028=0)
- 2.2E47 jnc short loc_0286
- 2.2E49 xor ax,ax ;; failed - return 0L
- 2.2E4B xor dx,dx
- 2.2E4D jmp short loc_0287 ; (2E53)
- 2.2E4F loc_0286: ; xref 2.2E47
- 2.2E4F mov dx,ax
- 2.2E51 mov ax,bx ;; success - return lock handle
- 2.2E53 loc_0287: ; xref 2.2E4D
- 2.2E53 pop bp
- 2.2E54 retn
- DevHlp_Lock endp
-
- Everywhere in the program that "sub_0030" is referenced, we can replace
- this with a name such as "DevHlp_Lock". This will immediately clarify,
- not only the code shown above, but every call to it. For example, after
- replacing all "sub_0030" with "DevHlp_Lock", one piece of the driver code
- looks like this:
-
- 2.0522 push 0
- 2.0524 push 0
- 2.0526 push word ptr [bp+6]
- 2.0529 call DevHlp_Lock ; (2E35)
- 2.052C add sp,6
- 2.052F les bx,dword ptr [bp+10h]
- 2.0532 mov es:[bx],ax
- 2.0535 mov es:[bx+2],dx
-
- Clearly, [bp+6] here holds a selector or segment value, and es:[bx] and
- es:[bx+2] at the end are expected to hold a lock handle. The lock is
- short-term/wait.
-
- If you go through this process for every DevHlp call, your assembly
- listing will make a lot more sense.
-
-
- Locating Request Handlers
- -------------------------
-
- An OS/2 device driver's Strategy routine receives request packets. (As
- noted earlier, Sourcer automatically labels all Strategy routines for
- you.) The request packet has, at offset 2, a command code. Some command
- codes are:
-
- 00h INIT (initialization)
- 01h Media Check
- 02h Build BPB (BIOS Parameter Block)
- 03h Reserved
- 04h Read (input)
- 05h Nondestructive Read
- 06h Input Status
- 07h Flush Input Buffers
- 08h Write (output)
- 09h Write with Verify
- 0Ah Output Status
- 0Bh Flush Output Buffers
- 0Ch Reserved
- 0Dh Device Open
- 0Eh Device Close
- 0Fh Removeable Media
- 10h Generic IOCTL
- 11h Reset Media
- 12h Get Logical Drive Map
- 13h Set Logical Drive Map
- 14h Deinstall Driver
- 15h Reserved
- 16h Partitionable Fixed Disk
- 17h Get Fixed Disk Map
- 18h Reserved
- 19h Reserved
- 1Ah Reserved
-
- The code in the Strategy function that handles these requests will often
- look something like this:
-
- 2.0011 mov al,es:[bx+2]
- 2.0015 cmp al,21h
- 2.0017 jbe short loc_0002
- 2.0019 mov al,21h
- 2.001B loc_0002: ; xref 2.0017
- 2.001B xor ah,ah
- 2.001D shl ax,1
- 2.001F mov si,ax
- 2.0021 push es
- 2.0022 push bx
- 2.0023 call word ptr data_0009[si] ;*(1.0066=2FD2h) 35 entries
- ...
-
- Here, data_0009 is a table of two-byte function pointers. The first
- entry contains the address of a function that handles INIT (0), the
- second entry contains the address of a function that handles Media Check
- (1), and so on. In many cases, multiple entries in the table will point
- to the same function. In the assembly listing, the table will often look
- something like this:
-
- 1.0066 data_0009 dw offset sub_0039 ; Data table (indexed access)
- 1.0068 data_0010 dw offset sub_0008 ; (2.0132)
- 1.006A data_0011 dw offset sub_0009 ; (2.0186)
- 1.006C data_0012 dw offset sub_0007 ; (2.012E)
- 1.006E data_0013 dw offset sub_0010 ; (2.01C6)
- 1.0070 data_0014 dw offset sub_0007 ; (2.012E)
- 1.0072 data_0015 dw offset sub_0007 ; (2.012E)
- 1.0074 data_0016 dw offset sub_0007 ; (2.012E)
- 1.0076 data_0017 dw offset sub_0011 ; (2.0364)
- 1.0078 data_0018 dw offset sub_0011 ; (2.0364)
- 1.007A data_0019 dw offset sub_0007 ; (2.012E)
- 1.007C data_0020 dw offset sub_0007 ; (2.012E)
- 1.007E data_0021 dw offset sub_0007 ; (2.012E)
- 1.0080 data_0022 dw offset sub_0006 ; (2.012A)
- 1.0082 data_0023 dw offset sub_0006 ; (2.012A)
- 1.0084 data_0024 dw offset sub_0006 ; (2.012A)
- 1.0086 data_0025 dw offset sub_0017 ; (2.03D2)
- ...
-
- At first glance, this isn't very useful. However, we said that the first
- entry in the table handles INIT (0), the second entry handles Media Check
- (1), and so on. Thus, we now know that sub_0039 is actually the INIT
- handler! All occurrences of sub_0039, here and elsewhere in the program,
- can be replaced with "INIT". Likewise, "sub_0008" is really
- "MediaCheck". Note how multiple entries use sub_0007; this ought to be
- replaced with a name such as "Reserved". When we're done, the table looks
- like this:
-
- 1.0066 data_0009 dw offset INIT ; Data table (indexed access)
- 1.0068 data_0010 dw offset MediaCheck ; (2.0132)
- 1.006A data_0011 dw offset BuildBPB ; (2.0186)
- 1.006C data_0012 dw offset Reserved ; (2.012E)
- 1.006E data_0013 dw offset Read ; (2.01C6)
- 1.0070 data_0014 dw offset Reserved ; (2.012E)
- 1.0072 data_0015 dw offset Reserved ; (2.012E)
- 1.0074 data_0016 dw offset Reserved ; (2.012E)
- 1.0076 data_0017 dw offset Write ; (2.0364)
- 1.0078 data_0018 dw offset Write ; (2.0364)
- 1.007A data_0019 dw offset Reserved ; (2.012E)
- 1.007C data_0020 dw offset Reserved ; (2.012E)
- 1.007E data_0021 dw offset Reserved ; (2.012E)
- 1.0080 data_0022 dw offset OpenClose ; (2.012A)
- 1.0082 data_0023 dw offset OpenClose ; (2.012A)
- 1.0084 data_0024 dw offset OpenClose ; (2.012A)
- 1.0086 data_0025 dw offset GenericIOCTL ; (2.03D2)
- ...
-
- A good double-check is that the code for INIT should contain (or at least
- indirectly call) the code that initializes the DevHlp function pointer:
-
- INIT proc near
- ...
- 2.301E les bx,dword ptr [bp+4]
- 2.3021 mov ax,es:[bx+0Eh]
- 2.3025 mov dx,es:[bx+10h]
- 2.3029 mov word ptr DevHlp,ax ; (1.0034=129h)
- ...
- INIT endp
-
- For more information on OS/2 device drivers, see the books by Duncan and
- Mastrianni referred to earlier in this note.
- ____________________________________________________________________________
- WINDOWS SOURCE (c) 1992 V Communications, Inc. All Rights Reserved.
-
-