Feedback Search Top Backward Forward

OS2LDR and OS2KRNL -- The Secret Handshake

Written by David C. Zimmerli




In my article Inside the OS/2 Kernel, I plodded through the initialization steps of the OS2LDR module as revealed on the debug terminal during system boot-up. In this article, I want to take a more structural view of OS2LDR, focusing especially on the interface between OS2LDR and OS2KRNL. To my knowledge, this interface, including about 50 "Dos Helper" functions, has never before been documented or even mentioned in any public materials.

I. The Conundrum

To recap briefly my earlier study: at OS/2 system initialization, the OS2LDR module, running in real mode, sets up the PIC chips, sets the PIT timer chips, queries the BIOS for hardware configuration data, reads various CMOS and ROM information, and finally loads (i.e., reads in and applies fixups to) OS2KRNL and transfers control to it at its start address, syiInitializeOS2.

This knowledge solves the problem of interpreting all the magic incantations pouring across the debug terminal during system start-up, but it also raises an important conceptual question: Why does the loader do all this? Shouldn't the loader just load the kernel, give it control, and go away? Why not let the kernel handle communicating with the BIOS, initializing the peripheral chips, and tweaking the system ports to produce a nicely tuned, smoothly humming operating system?

The plot thickens when we discover that, after initialization, the "bottom layer" of protected mode interrupt handling code-- the instructions to which the IDT points directly-- consists of code from OS2LDR! How is it that a piece of the kernel loader module survives in the fully initialized system, well after it should no longer be needed? And what in carnation does protected mode interrupt handling have to do with real mode kernel loading?

II. OS2LDR: More than just a Kernel Loader

It turns out that OS2LDR is much more than a loader for OS2KRNL. It also incorporates a library of about 50 call-back functions, known internally as "Dos Helper" functions, which the kernel uses both during and after initialization to talk to the PC hardware. This library, it seems, is the beginning of an attempt to insulate the core functions of the kernel-- task management, memory management, semaphore handling, and so on-- from the "bare metal" of the PC architecture-- the knowledge of which ports control which peripheral chips, how the BIOS data area is laid out, and the like. To those of you who have read the book on OS/2 for the PowerPC, this concept should start to sound familiar. Can you say "micro-kernel"?

In what follows I will attempt to document the "Dos Helper" functions. These presumably get their name from the fact that they help the kernel implement the Dos* API calls, or support CP-DOS, as the kernel used to be called. They are intended only for use by OS2KRNL, and of course must not be confused with the DevHelp, Virtual DevHelp, or FSHelp APIs.

III. The Interface Details

When OS2LDR passes control to OS2KRNL, it passes a pointer (in SS:BX) to a structure which I will call muKServiceStruct. Its layout is as follows:

Offset      Description

0           16:16 pointer to Arena Info Table
4           16:16 pointer to Boot Parameter Block
8           Physical boot drive # (80h = C, 81h = D, etc.)
9           Boot Mode Flags
0Ah         16:16 pointer to warm reboot entry point
0Eh         16:16 pointer to DosHelp function table
12h         zero
14h         devStrat entry point 

The Arena Info Table lists all memory objects that have been set up by OS2LDR, including all the kernel segments, the mini-FSD (i.e. OS2BOOT), OS2DUMP, OS2LDR itself, and empty arenas which are available for applications and device drivers. It is an array of structs of length 16h having the following layout:

Offset      Description

0           Starting physical address
4           Size
8           Starting virtual address
0Ch         Selector  
0Eh         flags
10h         object flags from the Object Table Entry in the load module
14h         "System Object Id" as given in the Debugging Handbook, Vol. IV,
            pp. 249 - 254

And the "DosHelp function table" has the following layout:

Offset      Description

0           Interface version # -- = 0008 for OS/2 2.1, 0009 for Warp 3, etc.
2           16:16 pointer to DosHelp function #1
6           16:16 pointer to DosHelp function #2
...         ... etc.

The Dos Helper functions can be separated into three categories: (1) those which are called during the real mode phase of system initialization, (2) those which are called during the protected mode phase of system initialization, and (3) those which can be called at any time after entering protected mode. The first group are addressed with real mode segment 9400 (for example; the exact value depends on the size of OS2LDR and the amount of real mode memory installed); the second group with code selector 0110h and data selector 0118h; and the third group with code selector 0100h and data selector 0108h.

Here is the interface for OS/2 2.1:

Function, Description

1,1       get PIC masks and RTC/Config RAM data
2,3       reboot
3,3       enable NMI
4,1       get ax=low mem, bx=high mem
5,1       get hardware equip word (# of COM ports, # of drives,
          # of parallel ports, math co-proc.)
6,2       set si,di to point to default BASEDEVs to be loaded
7,1       input dl; sets si to point to "diskette parameter info" 
          for drive dl
8         entry pt. to OS2DUMP
9         entry pt. to OS2DUMP
10,1      read sectors (for FAT boot)
11,3      debug comm routine
12,3      debug comm routine
13,3      debug comm routine
14,3      debug comm routine
15,3      sets PIT
16,3      turn on/off speaker
17,3      get PIC masks
18,3      set PIC masks
19,3      Real Time Clock access routine
20,3      Real Time Clock access routine
21,3      set video mode and initialize 8514 registers
22,3      stores SYSxxxx
23,1      writes SYSxxxx messages
24,2      set regs for system timer
25,3      set system timer (PIT #1, System Timer 0, counter 0)
26,3      enable watchdog timer
27,3      disable watchdog timer
28,2      set up IDT
29,2      mark end of loader stage 2 
30,2      apply fixups to protected mode interrupt handling code
31,3      32-bit 8259 PIC setting routine
32,3      32-bit 8259 PIC setting routine - shut off IRQ (input AX=IRQ#)
33,3      32-bit system timer (PIT #1, system timer 0, counter 0) routine
34,3      32-bit system timer (PIT #1, system timer 0, counter 0) routine
35,2      sets delay count for function #38
36,3      clear math coprocessor busy flag
37,3      shut off IRQ 13 (math coprocessor IRQ)
38,3      delay or clear task-switch flag
39,3      get math coprocessor IRQ status
40,3      initialize math coprocessor
41,3      stub function -- just a retd
42,3      prepares 'OS2 !! SYSxxxx' string
43,3      speaker beep
44,2      sets ESI = &(list of system DLL names), ecx=length of list
45,3      speaker beep
46,3      call PS/2 model-specific function
47,3      check for RAM parity error
48,3      just sets carry and returns
49,3      PS/2 only
50,3      PS/2 only
51,3      PS/2 only
52,3      PS/2 only

The interface for OS/2 Warp 3.0 adds 3 new functions, bringing the total to 55. In a future article I hope to document these new functions as well as the interface for Warp 4.

Of course, all the usual caveats apply here about the use of undocumented interfaces. However, this information should be a useful starting point for anyone wishing to write a custom loader-- or a custom kernel for that matter.

I welcome comments and questions about this article. Please email

Copyright © 1999, David C. Zimmerli. All rights reserved.