OS/2 initialisation - making it work for you

By Roger Orr

One of the biggest disappointments I first had with OS/2 was the number of times I needed to reboot my machine. In particular, I didn't like having to reboot TWICE very often - first to boot DOS off a floppy so I could update a DLL or check the disk and the second time to restart OS/2 to use the new environment.

There are four common reasons why I have to reboot from floppy (or at least with a changed CONFIG.SYS)


 * 1) to run CHKDSK:This is a fairly common problem with developers! When OS/2 stops with one of those wonderfully unhelpful messages ("the system is stopped") or more often when PM just hangs up on you forcing a reboot, then any open files are often not tidied up properly.
 * I am very often caught with compilations in progress or log files being written to, and so have to run CHKDSK. Unfortunately... you need exclusive use of the disk to do a good job and OS/2 tends to have lots of files assigned.
 * My system is set up with several logical drives, and drive C: is basically read-only (OS/2, C compiler, utility programs, archives) and I try to keep it that way to eliminate disk problems with it. However I do use other drives (for example for the swap file) and so cannot CHKDSK them once OS/2 is running.


 * 2) to update DLLs:As described by Adrian Thomson in the last issue of Pointers you can write DLLs which OS/2 treats as though they are part of the operating system. This is fine...until you wish to update one such DLL with a new version - you can't update the file while OS/2 has the DLL loaded. Even worse, if you have a bug in the DLL then PM may never initialise.
 * 3) to save/restore PM configurations:PM saves its configuration in files (OS2.INI for OS/2 1.1, OS2.INI and OS2SYS.INI for OS/2 1.2) which remain assigned while PM is running. I feel much happier with a backup copy of these important files, and sometimes need to replace the current copy with a backup.
 * 4) to remove bad device drivers:It is very hard to write device drivers without sometimes putting in a little bug causing the driver to abort during initialisation or shortly after! Once this occurs you can't continue, often you must reboot from diskette and remove the device driver before you can load OS/2 again.

In this article I will describe and discuss a simple device driver which I find very useful to have on my machine to cater for these instances. This device driver allows you to enter an OS/2 full screen session BEFORE the system is initialised.

This means that: An additional reason for using this device driver is because it can be instructive to examine the system while it initialises. You may be one of those people who need to know a bit more about how OS/2 hangs together, or you may just be curious! If you possess one of the various memory analysis programs for OS/2 it can be instructive to see what gets loaded when, and what state various subsystems are in.
 * 1) only the C: drive is in use so I can CHKDSK the others (even the swapper hasn't started at this point)
 * 2) Only the base OS/2 is running so new copies of 'system' DLLs and detached programs can be copied into the right places
 * 3) PM is not running so OS2.INI can be copied to and from without problem
 * 4) Not all device drivers are loaded, so a faulty one can be deleted or updated before it is loaded!

Although I now USE the device driver fairly often for the four reasons listed above, I of course initially WROTE it because I was curious! I had two questions in my mind; the first was 'can I write a device driver entirely in C?' and the second was 'which OS/2 functions are available during system initialisation?'

Overview of the device driver
How does this device driver work? First a brief reminder for the rusty-minded among you of how OS/2 initialises.

OS/2 loading starts with the kernel and the 'base' device drivers (these are the separate device drivers such as DISK01.SYS, etc. under OS/2 1.1 and the single merged device driver such as basedd01.sys under OS/2 1.2). The configuration file CONFIG.SYS is then processed. However this is NOT a one-pass process - for example all the SET commands have been processed before any DEVICE= statements are processed. All the DEVICE= and IFS= statements in the configuration are saved up and done towards the end of the initialisation. At this point the system environment is essentially complete. Each device driver is loaded and initialised in the order it was listed.

Finally the PROTSHELL and RUN statements are executed and the system grinds into life!

Device driver initialisation is performed by OS/2 loading the device driver as a dynamic link library and calling the initial entry point. The thread runs as a thread of the startup process and is like a normal OS/2 thread (with a few differences, of course, such as privilege level!) During initialisation the device driver code has a dual personality - it can do 'device driver' like things AND regular OS/2 like things.

The CDD.SYS device driver uses this feature to perform a simple OS/2 program task - it merely waits up to 5 seconds for a key to be pressed, and if ESCAPE is pressed then it calls DosExecPgm to start a full screen command prompt. When this command processor finishes (by the user typing EXIT), the device completes its initialisation.

The initialisation process carries on with subsequent device drivers as if nothing has happened, the system manager is started and the rest is history...


 * NOTE:If you are using an installable file system or RAM disk then you need to ensure that this device driver is in CONFIG.SYS after the ifs=c:\os2\hpfs.ifs and device=c:\os2\vdisk.sys statements. If you don't do this then the HPFS drive and/or the RAM drive will not be accessible!

Detailed comments on the device driver code
The device driver is written entirely in C (I use Microsoft C5.1 or C6.0, but I expect IBM C/2 is OK too), which is easy to do because it isn't "really" a device driver at all! To write a less trivial device driver in C would involve either inline assembler or a subroutine library. Note the various compiler/language 'workarounds' for this sample code.

Firstly C5.1 generates incorrect code for a FAR function with the _saveregs keyword, so I have NEAR function which does the work (C6.0 doesn't need this fix). I wish to save all registers since then I can load ES and BX off the stack without needing to use assembler!

Secondly, a device driver header ought to contain the offset of the strategy routine followed by the offset of the inter-device communication routine. I can't find a way to compile a static structure containing only the OFFSET of a far function, so I merged the strategy and IDC routine addresses into a single field called strategy! This won't cause any problems because the bit in the flag word telling OS/2 that IDC is enabled is not set so the selector value won't be treated as an IDC offset!

Lastly I define '_acrtused' to prevent any C runtime functions being called in. You CAN in fact use some of them in C device drivers, but you need to take care during linking to ensure that the device header stays at the front of the data segment and that the DATA segment comes BEFORE the CODE segments. The .DOSSEG assembler directive in some of the C runtime functions prevents them being used as this directive forces code to precede data! The moral is to examine the MAP file from the linker to check everything looks OK (and, of course, to have a working copy of the CDD device driver in your CONFIG.SYS to allow you to recover from failed device driver initialisation!)

The device driver returns error for all commands other than INIT. An alternative approach would be to uninstall the device driver but doing this gives a nasty error message during system initialisation!

I found by experiment that the KbdPeek call was required to get the subsequent KbdCharIn to work properly - presumably the keyboard subsystem isn't quite ready for us yet!

There is a documented list of 'officially' supported APIs available to device driver initialisation code. DosExecPgm is NOT on the list. However I take the form of the wording ("Those dynamic link functions available to a device driver a INIT-time include...") to mean that IBM will guarantee that, for example, DosOpen will work but that if DosExecPgm causes problems then they'll just say "it's not a documented feature". It works fine under 1.1 and 1.2 so I don't expect any problems... but you have been warned!

Creating and installing the CDD.SYS file
The compile command to do the trick is: cl /G2s /W3 cdd.c cdd.def /Fecdd.sys os2.lib where the CDD.DEF file contains information for the linker to make a loadable device driver. (See below.) I recommend copying the device driver to a separate directory on drive C: and then you add it to CONFIG.SYS (for example you might copy the CDD.SYS file to c:\drivers and add the line "device=c:\drivers\cdd.sys" to your configuration file. You might want to experiment with the location relative to other device drivers and see how much or the system you want to be running when you start the full screen command prompt.

Conclusion
OS/2 initialisation, like so much of OS/2, benefits from the open design of the operating system. Using simple device drivers like this one enables you to customise the initialisation process to suit your requirements without needing to write very much specialised code.

Module Definition File CDD.DEF for the C Device Driver The source code CDD.C for the C Device Driver Roger Orr 21-Aug-1990

Postscript - CDD.SYS
I have had several queries passed on to me following the Technical Tip in the September/October issue. These queries are still arriving so I thought it worth describing the three commonest problems to save any other people wasting time unnecessarily.

Firstly, the compilation command was in error. The correct command should be: cl /G2s /W3 cdd.c cdd.def /Fecdd.sys os2.lib The original command gave link errors L2025 and L2029 caused by the attempt to resolve references to stack checking.

Secondly, a misprint of pkt> instead of pkt-> (for example pkt>PktCmd not pkt->PktCmd) confused some programmers less familiar with C.

Thirdly, on some versions of OS/2, if OS/2 tracing is turned on the keyboard subsystem hangs when the call to KbdPeek is made.

I am sorry for the wasted time these three problems have caused some people, and hope those who persevered found it was worth while.