Sample Code Chunks, From a Device Driver
From EDM2
By IBM
This file contains sample code information for memory management. This sample code is provided on an "AS IS" basis and therefore, assistance with its contents is NOT supported.
INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS DOCUMENT "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT WILL IBM BE LIABLE FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO ANY LOST PROFITS, LOST SAVINGS OR ANY INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY YOU BASED ON A THIRD PARTY CLAIM.
;************************************************************************ The following code sections were pruned from a 2 GIGABYTE VIRTUAL RAM DISK driver developed by Peter Norberg, Consulting (CID 76260,3355). The driver allows for 3 sets of memory to be managed: (1) a list of physical memory buffers which are not normally accessed by OS/2, (2) non-swappable memory, managed by OS/2 (3) swappable memory, managed by OS/2 They show some techniques for access of FLAT memory under OS/2 2.0; at this point, the comments far from complete! I recommend careful reading to understand the code. I hereby release this set of sample code chunks into the public domain, and I expressly deny any liability for it! Use this at your own risk -- this is intended for educational and training use. Initial assumptions: During your INIT phase (or delayed, if you are allocating memory beyond the first 16 meg), allocate your FLAT memory pointer using the VMA_ALLOC command. This memory may be fixed or movable, non-swapping or swappable. Place the returned flat address into the global plSwapMem pointer, with the clSwapMem counter describing the size of the buffer. The following code will then demonstrate read/write access to that buffer. You must also allocate 2 selectors (selGDT and selGDTUser) for use by this code. Some simplifications are possible, depending on your application. This code assumes that you have allocated two selectors (selGDT and selGDTUser) for use by the code for data copies. You may elect to use the calls which generate results in DS and ES as temporary selectors. I chose the global selectors so that interrupts could be enabled while accessing the data, and to allow me to keep the buffers addressed for as long as I like. However, since I am using global selectors, I had to protect the code which filled the selectors from reentrancy during the times the selectors are active. All of this is managed from the SingleThread/UnSingleThread pair and the higher-order DrvAccessNextBuffer and its associated DrvMapUnlock tool. See the "Example" code at the end for a complete access/de-select example ************************************************************************ Define a structure which stays on the stack, as a way of accessing flat memory. This struction contains the lock mechanism for that memory ************************************************************************ SWAPACCESS STRUCT 2 ; swap plLockedRegion dd 0 ; if fLocked is non-0, this is the start of the locked region ulOffMem dd 0 ; Offset to memory start clTotLeft dd 0 ; Total amount of memory left to transfer cSingleThreaded dw 0 ; non-0 means we are single threaded idPhysMem dw 0 ; If phys mem access, this is non-0 flocked db 0,0 ; non-0 means memory is locked by this package achLock db 12 dup (0) ; Lock Handle SWAPACCESS ENDS ;************************************************************************ ; DATA block declarations ;************************************************************************ DGROUP group _DATA _DATA segment dword public use16 'DATA' ... Place you device header here ... plSwapMem dd 0 ; Gets LINEAR address in 2.0, of swap space cbSwapMem dd 0 ; count of memory in the swap region sem dd 0 ; Semaphore for serializing access to selectors selGDT dw 0 ; global selector to use for DISK buffer access selGDTUser dw 0 ; global selector to use for USER buffers _DATA ends _TEXT segment dword public use16 'CODE' assume cs:_TEXT, ds:DGROUP, es:NOTHING ;************************************************************************ ; SingleThread: Called with SI referencing the STACK BASED SWAPACCESS ; structure; which has already been cleared by the caller. ; Returns carry set if we were not allowed to go single threaded ;************************************************************************ SingleThread proc inc ss:[si.SWAPACCESS.cSingleThreaded] ; Set for recursive call cmp ss:[si.SWAPACCESS.cSingleThreaded],1 ; See if already here je @F ; nope: do the block clc ; else called recursively: ignore ret ; and leave @@: PUSHAD IFDEF NOSEM @@: CLI ; No ints for a while clc ; Set no error test byte ptr sem,1 ; See if blocked already jz @F ; nope: we now own it call FinishBlock ; else block for a while jnc @B ; if no error, retry mov ss:[si.SWAPACCESS.cSingleThreaded],0 ; Set not blocked mov ax,08103h ; set error STC ; force error report STI ; re-allow interrupts POPAD ret ; and return: done @@: mov byte ptr sem,1 ; take the semaphore over STI ; re-allow interrupts POPAD ret ; and return ELSE lea bx,sem ; get the sem address mov ax,ds ; from our dgroup mov cx,-1 ; set mov di,cx ; the timeout mov dl,DevHlp_SemRequest ; request the sem call [fpDevHlp] ; and call the processor jnc @F ; Worked OK mov ss:[si.SWAPACCESS.cSingleThreaded],0 ; Set not blocked mov ax,08103h ; set error @@: POPAD ret ; and return: done ENDIF SingleThread endp ;************************************************************************ ; UnSingleThread: Called with SI referencing the STACK BASED SWAPACCESS ; structure; which has been properlay maintained by caller and by ; SingleThread. Concept is: any unlock unlocks all! ;************************************************************************ UnSingleThread proc test ss:[si.SWAPACCESS.cSingleThreaded],-1 ; Set for recursive call jnz @F ; yep: we have been locked clc ; else not locked ret ; leave now @@: PUSHAD IFDEF NOSEM mov byte ptr sem,0 ; set sem is cleared call FinishUnBlock ; and unblock our code clc ; no errors ELSE lea bx,sem ; get the sem address mov ax,ds ; from our dgroup mov dl, DevHlp_SemClear ; clear it call [fpDevHlp] ; and call the processor ENDIF mov ss:[si.SWAPACCESS.cSingleThreaded],0 ; Set no longer locked POPAD ret ; return; all done UnSingleThread endp ;************************************************************************ ; V20FlatSS: Converts SS:SI into eax FLAT address. ; dl destroyed, returns Carry SET on error, CLEAR on OK. ;************************************************************************ V20FlatSS proc ; Called with SI ref. SS memory to convert to ; flat, on ret eax = address mov ax,ss ; Set to access movzx esi, si ; the the memory mov dl, DevHlp_VirtToLin ; then set to perform the Lin To Pagelist action call [fpDevHlp] ; and do the action ret ; And return as needed V20FlatSS endp ;************************************************************************ ; On entry, si references the SWAPACCESS structure ;************************************************************************ DrvMapUnlock proc USES eax edx bx mov bx,ss:[si.SWAPACCESS.idPhysMem] ; get the memory access ID or bx,bx ; See if locked jz @F ; nope mov ss:[si.SWAPACCESS.idPhysMem],0 ; else set no longer locked push si ; save SI for a moment mov si,PDNMEM_UNLOCK ; and call CallPdnMem ; call the routine to unlock pop si ; then @@: push si ; restore SI test ss:[si.SWAPACCESS.fLocked],-1 ; See if locked jz @F ; not locked! mov ss:[si.SWAPACCESS.fLocked],0 ; set no longer locked lea si,ss:[si.SWAPACCESS.achLock] ; Get the lock handle call V20FlatSS ; the page list flat address jc @F ; if error, ignore mov esi,eax ; else mov dl,DevHlp_VMUnlock ; and set call [fpDevHlp] ; to unlock the memory @@: pop si ; restore ref to SWAPACCESS call UnSingleThread ; And turn off single thread, if needed ret ; Return: done DrvMapUnlock ENDP ;************************************************************************ ;DrvAccessNextBuffer: Map the sector passed into a temporary physical pointer ; in es:di or ds:si: ; ; On entry: ; ; DH is 0 for mapping into ds:si, ; 1 for mapping into es:di, 0 for ds:si ; 2 means do NOT assign ds or es to resulting selector ; 3 means we will write to the selector, but do not set ES or DS ; ; SI = reference to the SWAPACCESS array on entry, ; in the SS group ; ; On exit, ; DX is unchanged ; if DH was 0 or 1, the selected register pair (es:di or ds:si) are updated ; to ref the selGDT selector ; else ES, DS, SI, DI are unchanged on exit. ; ECX is the count you can access this time through. ; ; Side Effects: SWAPACCESS structure updated to reflect current conditions, ; if access legal, you will be in SINGLE THREADED mode (i.e., ; re-entrancy to the subsystem is blocked), and selGDT will ref ; the selected memory. ; ; ; WARNING! Must be called with DS referencing our data segment! ;************************************************************************ DrvAccessNextBuffer proc near LOCAL fbDH: WORD, cbReq: WORD, ulOldDI: DWORD, npSwpa: WORD mov fbDH,dx ; Save the storage flag mov npSwpa,si ; save low half of SI; access to SWAPACCESS array mov ulOldDI,edi ; save EDI call DrvMapUnlock ; Unlock prior access mov ecx,ss:[si.SWAPACCESS.clTotLeft] ; 0 extend the count cmp ecx,MAX_BLOCK_XFER ; see jbe @F ; if at end mov ecx,MAX_BLOCK_XFER ; nope: set upper limit @@: mov eax, ss:[si.SWAPACCESS.ulOffMem] ; get the memory ref add eax,plSwapMem ; Here on in the SWAP region: cmp ecx,cbSwapMem ; see if legal ja DrvMapBadReq ; OK mov cbReq,cx ; save count really requested mov ebx,eax ; for later use mov ss:[si.SWAPACCESS.plLockedRegion],eax ; Save for later free mov si,npSwpa ; re-get access to swap info lea si,ss:[si.SWAPACCESS.achLock] ; Set for extract of lock handle call V20FlatSS ; lock page list rel to SS jc DrvMapOops ; FAILED: forget it mov esi,eax ; save the lock handle sub eax,eax ; Lock in place, short term mov edi,-1 ; set no page list is to be generated test byte ptr fbDH+1,1 ; see if write planned jz @F ; nope mov ax,8 ; yep: force "WE ARE WRITING" bit @@: mov dl,DevHlp_VMLock ; and call [fpDevHlp] ; perform the lock jc DrvMapOops ; FAILED: forget it mov si,npSwpa ; restore ref to info mov ss:[si.SWAPACCESS.fLocked],1 ; Else set LOCKED call SingleThread ; force into single threaded mode jc DrvMapOops ; OOPS: No single thread! forget it mov ax,selGDT ; get the selector ;EBX is still the linear address of the buffer ; ECX is the length of the buffer mov dl,DevHlp_LinToGDTSelector ; convert to GDT selector call [fpDevHlp] ; do the conversion jc DrvMapOops ; FAILED the mapping mov si,npSwpa ; PHYS! restore ref to info mov edi,ulOldDI ; restore EDI mov dx,fbDH ; restore results flag movzx ecx,cbReq ; and correct the count for excess add ss:[si.SWAPACCESS.ulOffMem],ecx ; calculate the next access offset sub ss:[si.SWAPACCESS.clTotLeft],ecx ; and the total left to transfer cmp dh,2 ; are we all done? jae DrvMapOkRet ; yep: so exit or dh,dh ; see who gets what jz @F ; if 0, DS:SI sub di,di ; get the SI mov es,selGDT ; so get it DrvMapOkRet: clc ; set good access ret ; and return @@: ; HERE on results in DS:SI sub si,si ; get the SI mov ds,selGDT ; DESTRY DS ret ; and return DrvMapBadReq: mov eax,0AA55h ; set special error code mov si,npSwpa ; get the handle to the lock info call DrvMapUnlock ; go unlock it stc ; FAILED ACTION ret ; return quickly DrvAccessNextBuffer endp ;************************************************************************ ; Example code section which accesses some of the above ; Entered with: ; dx = Read (0) or write (1) flag ; eax = PHYSICAL address of user buffer ; ebx = offset from start of our FLAT buffer to access ; ecx = count of bytes to access ;************************************************************************ Example proc near LOCAL fReadOrWrite: WORD, fpbfr: DWORD, swpa: SWAPACCESS mov fReadOrWrite, dx ; ON ENTRY, dx = READ/WRITE flag ; 0 read, 1 write mov fpbfr,eax ; eax = pointer to buffer to access mov edx,ecx ; save count of bytes lea di,swpa ; set mov cx, sizeof swpa ; to init sub ax,ax ; the cld ; swap info structure push ss ; as pop es ; needed rep stosb ; to all 0 ... Now have code calculate where in the buffer it needs to access: ... set swpa.ulOffMem to the offset to the start of the portion of the buffer, ... set swpa.clTotLeft to the total count of bytes left to access mov swpa.clTotLeft,edx ; save the count for access mov swpa.ulOffMem,ebx ; save the memory offset from our ; flat buffer DrvRdLp: ; PROCESS LOOP: loop here until transfer is done cmp swpa.clTotLeft,0 ; see if any left je DrvRWDone ; if not, leave mov dh,2 ; Set do NOT change es/ds add dh,byte ptr fReadOrWrite ; convert to 3, for write lea si,swpa ; get the address of the info call DrvAccessNextBuffer ; do the mapping, LOCK THREAD jc DrvRwBadSec ; could not lock/map! mov ax,word ptr fpbfr+2 ; get the user buffer mov bx,word ptr fpbfr ; physical address mov si,selGDTUser ; set target GDT call PhysToGDTSel ; into our selector jc DrvRwBadSec ; could not generate the mapping! push ds ; Save our current segment test byte ptr fReadOrWrite,-1 ; See if READ or WRITE jnz @F ; WRITE: Process as needed mov es,selGDTUser ; USER pointer and mov ds,selGDT ; and the memory base jmp DrvRdWrCpy ; go do the copy @@: ; HERE for WRITE action set-up mov es,selGDT ; and the memory base mov ds,selGDTUser ; get the USER selector DrvRdWrCpy: assume DS:NOTHING mov bx,cx ; Save the transfer count sub di,di ; set for target offset and sub si,si ; source offsets of 0. cld ; Force correct string direction rep movsb ; and transfer the buffer ; OPTIMIZATION NOTE: The above could have a SHR CX,2 \ REP MOVSD ; if it is known that the buffer size is DWORD oriented; ; or similar in-line code could detect that condition pop ds ; Restore DS assume DS:DGROUP ; and ; NOTE: THIS IS WHERE TC_YIELD SHOULD BE, IF USED! mov ax,bx ; calculate add word ptr fpBfr,bx ; to form the adc word ptr fpBfr+2,0 ; next user buffer address to access jmp DrvRdLp ; do next DrvRWDone: ; HERE on all done lea si,swpa ; get the handle to the lock info call DrvMapUnlock ; unlock the memory CLC ; Set OK ret ; and report as needed DrvRWFailed: pop ds ; Restore DS DrvRWBadSec: lea si,swpa ; get the handle to the lock info call DrvMapUnlock ; unlock the memory mov ax,8103h ; Set DRIVE NOT READY stc ; Set ERROR ret ; and report in Example endp _TEXT ends end