Written by Roman Stangl
OS/2 may be rebooted by using the SETBOOT utility, that is shipped with OS/2. However when adding a reboot feature in your application you may want to avoid the effort starting another session, as you can easily do the same yourself.
Using the DOS.SYS device driver
You can easily implement the way SETBOOT works, you just need to open the device DOS$ and make an IOCTL-call, however this call is undocumented. Both, the following code using DOS$ and SETBOOT, require that the device driver DOS.SYS is included in your CONFIG.SYS (as you can only get access to the reboot-vector from ring-3 by a device driver, and DOS.SYS provides that functionality).
If you have not installed the device driver DOS.SYS (which for example the IBM EWS BOOTOS2 package doesn't), add it to your CONFIG.SYS (where d: is your boot drive):
apiretDosDD=DosOpen("\\DEV\\DOS$", &hfileDosDD, &ulAction, 0, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, 0); if(apiretDosDD!=NO_ERROR) ...Figure 2.1.a
Once you have opened the DOS$ device, you might inform the user that a ShutDown is in progress and finally shut down the file system by calling the API DosShutdown(). DosShutdown() will not stop processes from running, but file system calls will no longer work (therefore we had to open the device DOS$ before, as DosOpen is a file system call). Not having tested it, I would say threads making file system calls will either simply block or fail. Figure 2.1.b shows the call to DosShutdown(), though its quite well documented in the online reference.
/* Shutdown the system anyway */ DosShutdown(0);Figure 2.1.b
Having shut down OS/2, you might again inform the user what's going on. You can do this for example by updating a text in a message box, just ensure that this text is already loaded into memory (and hope that is hasn't been paged out of memory, but this is very very unlikely, I personally have never experienced such a problem).
You jump to the reboot vector by calling a function of the DOS$ device as shown in Figure 2.1.c.
if(NO_ERROR!=DosDevIOCtl(hfileDosDD, IOCTL_DOS, DOS_REBOOT, NULL, 0, NULL, NULL, 0, NULL)) ...Figure 2.1.c
Last but not least, Figure 2.1.d shows the parameters for the undocumented interface of the DOS$ device to call the reboot vector,
#define IOCTL_DOS 0x00D5 #define DOS_REBOOT 0x00ABFigure 2.1.d
In case something didn't work you also want to close the DOS$ device, and inform the user.
Using IOPL segments
Every workstation can also be rebooted by programming the keyboard controller and for at least MicroChannel PC's also by using the system control port A. The programming requires I/O and therefore must be done from a ring-2 IOPL segment (unless you are using the advanced methods to access I/Os, that is device drivers, call gates or ring-3 IOPL segments which have been described in other issues of EDM/2. I strongly discourage using this approach, because for me it didn't work reliably (e.g. ThinkPads seem to ignore this completely, most likely due to some power management hardware, I know that the code is processed for sure because I traced processing to the I/O instructions with the kernel debugger) and IOPL segments are non portable 16 bit code that is likely to be unsupported in the future.
Anyway, implementing it I found the solution of some interesting problems, so I've included it here too.
Let's start by a short look into Figure 3.1.a which shows the assembler code to reboot a workstation by trying to force activation of the reset line:
;******************************************************************** ;* PC2.c ;* Copyright (C) by Stangl Roman, 1993, 1994, 1995 ;* This Code may be freely distributed, provided the Copyright isn't ;* removed, under the conditions indicated in the documentation. ;* ;* Reboot.asm IOPL segment to reboot OS/2 by trying to hardware ;* reset the system by system control port A and by ;* programming a keyboard controller reset. ;* Unfortunately this does not work always, e.g. doesn't ;* work on a IBM ThinkPad 701, doesn't work reliably on ;* a PS/2 77i but perfectly works on a IBM PC 750. I ;* think this is due to a hardware incompatibility to ;* support features as APM (Advanced Power Management) ;* ;* Use the function prototype: ;* ;* extern USHORT _Far16 _Pascal RebootSystem(USHORT usPerformReset); ;* ;* ;* Add to your module definition file: ;* ;* SEGMENTS ;* CODEIOPL CLASS 'CODE' IOPL PRELOAD ;* ;* EXPORTS ;* RebootSystem=REBOOTSYSTEM 1 ;* ;******************************************************************** CODEIOPL SEGMENT PARA PUBLIC USE16 'CODE' ASSUME CS:CODEIOPL, DS:NOTHING db "@(#) $Header: Reboot.asm Version 2.00 05,1995 $ (LBL)" ; MACRO to let the processor loop after IN, OUT instructions IOWAIT MACRO Local IOW_Loop JMP $+2 PUSH CX MOV CX,010h IOW_Loop: LOOP IOW_Loop POP CX ENDM ; System control port A (see PS/2 technical references) CONTROLPORTA EQU 092h ; AND mask to toggle reset bit (bit 0) of control port A off RESETOFF EQU 0FEh ; OR mask to toggle reset bit (bit 0) of control port A on RESETON EQU 001h ; Keyboard controller status port (reading) KBDSTATUS EQU 064h ; Input buffer full INPUTBUFFERFULL EQU 002h ; Keyboard command pulse output port CMDPULSEOUTPUTPORT EQU 0FEh Stackframe struc ; Structure for arguments passed on stack dw 0h ; BP+00 : Saved BP dw 0h ; BP+02 : IP of return address dw 0h ; BP+04 : CS of return address arg1 dw 0h ; BP+06 : First parameter passed arg2 dw 0h ; BP+08 : Second parameter passed Stackframe ends ;*-----------------------------------------------------------------*\ ;* This function programs the system control port A and the keyboard ;* controller to reset the system via activation the reset line (both ;* ways are used to increase the probability it works). This function ;* must be in an segment having IOPL privileges because the control ;* port A and the keyboard controller is programmed via I/O ;* instructions. ;*-----------------------------------------------------------------*/ PUBLIC REBOOTSYSTEM ; USHORT RebootSystem(USHORT argument 1) REBOOTSYSTEM PROC FAR PUSH BP MOV BP, SP MOV AX, Stackframe.arg1[BP] ; Access argument 1 XOR AX, 0FFFFh ; Return failure CMP AX, 0FFFFh ; ? If argument 1 is FALSE return with failure ; to allow caller to call our code for diagnostics JE RS_Return IN AL, CONTROLPORTA ; Get system control port A IOWAIT MOV CX, 01000h ; Retry counter RS_SystemReset: AND AL, RESETOFF ; Toggle reset bit 0 off, on and off OUT CONTROLPORTA, AL IOWAIT OR AL, RESETON OUT CONTROLPORTA, AL IOWAIT AND AL, RESETOFF OUT CONTROLPORTA, AL IOWAIT LOOP RS_SystemReset ; Try a few times RS_WaitForKbd: MOV CX, 01000h ; Retry counter IN AL, KBDSTATUS ; Get keyboard status IOWAIT TEST AL, INPUTBUFFERFULL ; ? If keyboard buffer is empty, reset system JZ RS_PulseReset IOWAIT LOOP RS_WaitForKbd MOV AX, 0FFFFh ; Return failure JMP RS_Return RS_PulseReset: MOV AL, CMDPULSEOUTPUTPORT ; Pulse system reset port OUT KBDSTATUS, AL IOWAIT MOV AX, 0h ; Return success JMP RS_WaitForKbd RS_Return: POP BP RET 2 ; Clear stack from parameters passed REBOOTSYSTEM ENDP CODEIOPL ENDS ENDFigure 3.1.a
You may note that an argument is passed to this ring-2 function call. This will be explained in Figure 3.1.c in more detail.
Many articles have already covered calling ring-2 code from ring-3, so Figure 3.1.b is only included for completeness.
SEGMENTS CODEIOPL CLASS 'CODE' IOPL PRELOAD EXPORTS RebootSystem=REBOOTSYSTEM 1Figure 3.1.b
The most interesting thing is shown in Figure 3.1.c. You will surely notice, that RebootSystem() is called twice, once with the parameter FALSE and once with TRUE. RebootSystem(FALSE) calls the code in the IOPL segment without actually doing I/O programming to reset your workstation, however calling code in the IOPL segment ensures that the code is in memory, that is not paged out into the swapper or not loaded at all.
If we didn't do this, DosShutdown() would flush and close the file system, and if the IOPL segment wasn't in memory before calling DosShutdown() it would never be paged in or loaded. Of course, as only device drivers can lock memory, there is the possibility, that the IOPL segment gets paged out between the calls to RebootSystem(FALSE) and DosShutdown() but I would expect this to be very unlikely.
Finally, RebootSystem(TRUE) is called to perform the hardware reset.
/* Call function RebootSystem() from our IOPL segment to ensure that all necessary code gets loaded now, because after DosShutdown(), no disk access is allowed anymore. Leaving out this call prevents the reboot from working (above explanation is what I assume, I don't know it exactly) */ RebootSystem(FALSE); DosShutdown(0); RebootSystem(TRUE);Figure 3.1.c
I've excluded the code that informed the user on a dialog about the progress, and some DosSleep calls that gave the user some time to read messages. You may argue that DosSleep in a dialog procedure blocks PM (because blocking the message queue) and you are right, but once you have called DosShutdown() user input doen't make much sense anyway.
The undocumented DOS$ call has been published in the OS/2 development fora on the IBM PC conference disk and the InterNet NetNews. IPLing a PC through the system control port A and the keyboard controller has been published in verious IBM technical references.