Jump to content

SMPProgAdd - Platform Specific Drivers (PSDs): Difference between revisions

From EDM2
Line 227: Line 227:
---
---
= PSD Functions =
= PSD Functions =
Following are the PSD functions.  
Following are the PSD functions.  


Line 244: Line 243:
* [[PSD_APP_COMM]]
* [[PSD_APP_COMM]]
* [[PSD_SET_ADV_INT_MODE]]
* [[PSD_SET_ADV_INT_MODE]]
== PSD_INSTALL ==
;'''PSD_INSTALL keywords'''
Required, Can Block
;'''Description'''
Determine if the PSD supports the current platform.
This function probes the hardware to see if the PSD supports the current
platform. No other operations should be executed in this function. It is merely
a presence check. This function is the first function called upon loading a
PSD. It must store away all the information passed to it in the install
structure.
;'''Mode'''
Called in Init Mode; may be called in Kernel Mode.
;'''Entry'''
Pointer to an INSTALL structure.
;'''Exit'''
* NO_ERROR if the PSD installed successfully.
* -1 if the PSD does not support the current platform.
;'''Structures'''
<pre>
typedef struct install_s
{
   P_F_2   pPSDHlpRouter;  (Input)
   char   *pParmString;    (Input)
   void   *pPSDPLMA;       (Input)
   ulong_t sizePLMA;       (Input)
} INSTALL;
</pre>
pPSDHlpRouter points to the PSD help router. Use the PSDHelp macro in PSD
.H to access the PSD helps.
pParmString points to any parameters specified in CONFIG.SYS after the
PSD's name. If no parameters were specified this field is NULL.
pPSDPLMA points to the PSD's processor local memory area. This area
contains different physical memory at the same linear address across all processors.
You can use the area to store variables such that each processor accesses unique
values, but all the code references the same variables.
sizePLMA is the total size of the PSD's PLMA in bytes.
;'''Notes'''
This function may be called after OS/2 is finished with initialization
by the DosTestPSD API; therefore, the PSD developer must be careful not to use any
Init mode only PSD help's in this function.
== PSD_DEINSTALL ==
;'''PSD_DEINSTALL keywords'''
Required, Can Block
;'''Description'''
DeInstall the PSD.
This function is called to release any resources that may have been allocated
by the PSD_INSTALL function. A PSD is never de-installed after its Init
routine is called.
;'''Mode'''
Called in Init Mode; may be called in Kernel Mode.
;'''Entry'''
None.
;'''Exit'''
* NO_ERROR if the PSD DeInstalled succesfully.
* -1 if the PSD didn't DeInstall.
;'''Notes'''
This function may be called after OS/2 is finished with initialization
by the DosTestPSD API; therefore, the PSD developer must be careful not to use any
init mode only PSDHelp's in this function.
== PSD_INIT ==
;'''PSD_INIT keywords'''
Required, Can Block
;'''Description'''
Initialize the PSD.
This function is called to initialize the PSD. It is used to allocate and
initialize any resources that the PSD may require, as well as initializing the state of
the hardware. This function should only initialize the state of the hardware in
general. Initialization of CPUs should be done in ProcInit. It must fill in the
INIT structure passed to it by OS/2. This function is only called once on CPU0
.
;'''Mode'''
Called in Init Mode only.
;'''Entry'''
Pointer to INIT structure
;'''Exit'''
* NO_ERROR if the PSD initialized successfully.
* -1 if the PSD didn't initialize.
;'''Structures'''
<pre>
typedef struct init_s
{
   ulong_t flags;    (Output)
   ulong_t version;  (Output)
} INIT;
</pre>
flags in the INIT structure indicate any special features or requirement that
the PSD may have.
* INIT_GLOBAL_IRQ_ACCESS indicates that the platform can perform IRQ operations (e
.g. PIC masking) on any processor. If this flag is omitted, the IRQ
functions are guaranteed to only get called on CPU0, otherwise they may get called on any
processor. If the flag is omitted and an IRQ operation is initiated on a processor
other then CPU0, the OS/2 kernel will route the request to CPU0.
* INIT_USE_FPERR_TRAP indicates that Trap 16 will be used to report floating
point errors, instead of IRQ 13. If this flag is set, the kernel sets the NE flag
in CR0 for all processors. The PSD is responsible for doing any additional
work for making the transition.
* INIT_EOI_IRQ13_ON_CPU0 indicates that an EOI for a floating point error using
IRQ13 should only be performed from CPU0. On CPU1-N, the hardware is responsible
for clearing the interrupt.
version indicates the version number of this PSD. It should be updated
appropriately as this will help with service.
;'''Notes'''
None.
== PSD_PROC_INIT ==
;'''PSD_PROC_INIT keywords'''
MP, Can Block
;'''Description'''
Initialize the current processor.
This function is called to initialize the current processor. It is called
in protect mode, once on a per-processor basis. It should initialize variables
in the PSD's PLMA, along with initialization of the hardware state for that
specific processor.
;'''Mode'''
Called in Init Mode only.
;'''Entry'''
None.
;'''Exit'''
* NO_ERROR if the processor initialized successfully.
* -1 if the processor didn't initialize.
;'''Structures'''
None
;'''Notes'''
None
== PSD_START_PROC ==
;'''PSD_START_PROC keywords'''
MP, Can Block
;'''Description'''
Start a processor.
This function is used to start a specified processor. The PSD may only
start the processor that was specified.
OS/2 fills in the address of a started processors initial real mode CS:
IP in the warm reboot vector of the BIOS data area (0x40:0x67).
OS/2 provides serialization such that another processor will not be started
until the previous processor has finished its real mode initialization, gone into
protect mode, and finished calling the ProcInit function. The processor which is
started will be held in real mode until the StartProc function has been completed, and
will then be allowed to initialize.
All processors are started before the first device driver is loaded.
;'''Mode'''
Called in Init Mode only.
;'''Entry'''
Processor number (0-based).
;'''Exit'''
* NO_ERROR if the processor started successfully.
* -1 if the processor didn't start.
;'''Structures'''
None.
;'''Notes'''
If the hardware implementation uses some other mechanism to indicate a
started processors initial CS:IP the value specified in the warm reboot vector
should be used.
If the hardware implementation requires some other real mode operation to be
completed before the processor can continue to execute, the PSD developer must be certain
to chain to the address specified in the warm reboot vector.
== PSD_GET_NUM_OF_PROCS ==
;'''PSD_GET_NUM_OF_PROCS keywords'''
Required, Can Block
;'''Description'''
Return number of processors.
This function must detect and return the number of usable x86 based
processors that exist on the current platform. If the PSD detects that any of the
processors are defective or non x86-based, it is the PSD's responsibility to setup
the state of the PSD and hardware, such that all usable processors are logically
ordered. For example, if there are 4 processors and CPU2 is defective, the CPU
's should be ordered as follows: CPU0 = 0, CPU1 = 1, CPU2 (Defective), CPU3
= 2).
;'''Mode'''
Called in Init Mode only.
;'''Entry'''
None.
;'''Exit'''
Number of processors (1-based).
;'''Structures'''
None.
;'''Notes'''
OS/2 Warp Server for SMP only supports processors that are compatible
with the architecture of the Intel 386 and above.
== PSD_GEN_IPI ==
;'''PSD_GEN_IPI keywords'''
MP, Can't Block
;'''Description'''
Generate an inter-processor interrupt.
This function is used to generate an inter-processor interrupt. All inter
-processor hardware dependencies should be fully initialized before the first
GenIPI is called.
;'''Mode'''
Called in Kernel, and Interrupt Mode.
;'''Entry'''
Processor number to interrupt (0-based).
;'''Exit'''
* NO_ERROR if the IPI was generated.
* -1 if the IPI was not generated.
;'''Structures'''
None.
;'''Notes'''
OS/2 guarantees that the GenIPI function will not be called to
interrupt a processor that has not finished processing any previous IPIs.
== PSD_END_IPI ==
;'''PSD_END_IPI keywords'''
MP, Can't Block
;'''Description'''
End an inter-processor interrupt.
This function is used to end an inter-processor interrupt, that was generated
by GenIPI.
;'''Mode'''
Called in Kernel, and Interrupt Mode.
;'''Entry'''
Processor number to end interrupt on (0-based).
;'''Exit'''
* NO_ERROR if the IPI was ended successfully.
* -1 if the IPI didn't end successfully.
;'''Structures'''
None.
;'''Notes'''
The processor number specified and the current processor number should
be identical.
== PSD_PORT_IO ==
;'''PSD_PORT_IO keywords'''
Optional, Default, Can't Block
;'''Description'''
Perform local port I/O.
Some platforms have some non MP specific system ports localized on a per-
processor basis. If a local I/O operation may block before completion, I/O can be
routed to a specific CPU for processing. This should be done, because an operation
which started on one processor is not guaranteed to complete on that processor if
execution is blocked. This function gets invoked as the result of a device driver
calling DevHelp_Port_IO.
;'''Mode'''
Called in Kernel, and Interrupt Mode.
;'''Entry'''
Pointer to a PORT_IO structure.
;'''Exit'''
* NO_ERROR if the I/O was successful.
* -1 if the I/O wasn't successful.
;'''Structures'''
<pre>
typedef struct port_io_s
{
   ulong_t port;   (Input)
   ulong_t data;   (Input/Output)
   ulong_t flags;  (Input)
} PORT_IO;
</pre>
port indicates which port to read to, or write from.
data contains the data read from a read request, or the data to write if a
write request. If the request uses less the 4 bytes the least significant portion
of the data variable is used.
flags indicate what operation to perform.
* IO_READ_BYTE Read a byte from the port
* IO_READ_WORD Read a word from the port
* IO_READ_DWORD Read a dword from the port
* IO_WRITE_BYTE Write a byte to the port
* IO_WRITE_WORD Write a word to the port
* IO_WRITE_DWORD Write a dword to the port
;'''Notes'''
If the I/O performed is to a non-local port, the I/O should be handled
as a regular I/O request.
If device drivers or applications access the local ports directly, instead of
using the documented interfaces problems may occur.
== PSD_IRQ_MASK ==
;'''PSD_IRQ_MASK keywords'''
Optional, Default, Can't Block
;'''Description'''
Mask/Unmask IRQ levels
This function allows masking (disabling), or un-masking (enabling) of IRQ
levels. When this function is invoked it should save the state of the interrupt
flag, and disable interrupts before performing the mask operation. It should
then restore the state of the interrupt flag.
;'''Mode'''
Called in Kernel, and Interrupt Mode.
;'''Entry'''
Pointer to PSD_IRQ structure.
;'''Exit'''
* NO_ERROR operation completed successfully.
* -1 operation failed.
;'''Structures'''
<pre>
typedef struct psd_irq_s
{
   ulong_t flags;    (Input)
   ulong_t data;     (Input/Output)
   ulong_t procnum;  (Input)
} PSD_IRQ;
</pre>
data is the logical IRQ levels to mask, or un-mask.
flags indicate which type of operation is to be performed.
* IRQ_MASK mask (disable) IRQ levels
* IRQ_UNMASK unmask (enable) IRQ levels
* IRQ_GETMASK retrieves the masks for all IRQ levels
* IRQ_NEWMASK indicates that all the IRQ levels should reflect the state of the
specified mask.
procnum is the processor number of where the operation should take place.
;'''Notes'''
If this function is omitted, OS/2 will perform all mask operations for
an 8259 Master/Slave based PIC system. The requests will be sent to CPU0
depending on the state of the INIT_GLOBAL_IRQ_ACCESS flag.
== PSD_IRQ_REG ==
;'''PSD_IRQ_REG keywords'''
Optional, Default, Can't Block
;'''Description'''
Access IRQ related registers.
This function permits access to the IRQ related registers.
;'''Mode'''
Called in Kernel, and Interrupt Mode.
;'''Entry'''
Pointer to PSD_IRQ structure.
;'''Exit'''
* NO_ERROR operation completed successfully.
* -1 operation failed.
;'''Structures'''
<pre>
typedef struct psd_irq_s
{
   ulong_t flags;    (Input)
   ulong_t data;     (Input/Output)
   ulong_t procnum;  (Input)
} PSD_IRQ;
</pre>
flags indicate which type of operation is to be performed.
* IRQ_READ_IRR read the interrupt request register.
* IRQ_READ_ISR read the in service register.
data contains the data read from a read request, or the data to write if a
write request.
procnum is the processor number of where the operation should take place.
;'''Notes'''
If this function is omitted, OS/2 will perform all register operations
for an 8259 Master/Slave based PIC system. The requests will be sent to CPU0
depending on the state of the INIT_GLOBAL_IRQ_ACCESS flag.
== PSD_IRQ_EOI ==
;'''PSD_IRQ_EOI keywords'''
Optional, Default, Can't Block
;'''Description'''
Issue an EOI.
This function is used to issue an End-Of-Interrupt.
;'''Mode'''
Called in Kernel, and Interrupt mode.
;'''Entry'''
Pointer to PSD_IRQ structure.
;'''Exit'''
* NO_ERROR operation completed successfully.
* -1 operation failed.
;'''Structures'''
<pre>
typedef struct psd_irq_s
{
   ulong_t flags;    (Input)
   ulong_t data;     (Input/Output)
   ulong_t procnum;  (Input)
} PSD_IRQ;
</pre>
data is the interrupt level to end.
flags is not used in this operation.
procnum is the processor number of where the operation should take place.
;'''Notes'''
If this function is omitted, OS/2 will perform all EOI operations for
an 8259 Master/Slave based PIC system. The requests will be sent to CPU0
depending on the state of the INIT_GLOBAL_IRQ_ACCESS flag.
== PSD_APP_COMM ==
;'''PSD_APP_COMM keywords'''
Optional, Can Block
;'''Description'''
Perform generic APP/PSD communication.
This function performs generic application/PSD communication. The entry
arguments, and return codes are not interpreted by OS/2, it is passed verbatim to and
from the PSD.
;'''Mode'''
Called in Kernel mode.
;'''Entry'''
Function number, Argument.
;'''Exit'''
Return code.
;'''Structures'''
None.
;'''Notes'''
None.
== PSD_SET_ADV_INT_MODE ==
;'''PSD_SET_ADV_INT_MODE keywords'''
Optional, Can't Block
;'''Description'''
TBD
;'''Mode'''
Called in Init Mode only.
;'''Entry'''
None.
;'''Exit'''
Return code.
;'''Structures'''
None.
;'''Notes'''
The kernel initially provides default handling/detection for spurious
interrupts. This is done for the last IRQ line of every PIC. It does this by
checking the PIC's ISR register, and if the IRQ is not in service, it does not pass
the interrupt request to the interrupt manager (i.e. a spurious interrupt)
.
If a PSD switches into advanced interrupt mode; the kernel will no longer
provide default handling/detection of spurious interrupts. It becomes the PSD's
responsibility.
One way a PSD could provide handling/detection of a spurious interrupt is to
register a PSD handler for an IRQ level which may be spurious. As soon as the
interrupt is detected the handler should insure that it is valid. If it is not (i
.e. a spurious interrupt), it should dismiss the interrupt, and return NO_
ERROR to the interrupt manager. The NO_ERROR return code informs the interrupt
manager that the interrupt has been handled by the PSD. If the interrupt is valid
the PSD should return a -1, as this informs the interrupt manager that the
interrupt should be passed on to any device drivers registered to receive that interrupt.


= PSD Helps =
= PSD Helps =

Revision as of 18:17, 18 May 2025

In OS/2 Warp Server for SMP, all of the platform specific code has been removed from the operating system, and placed into a Platform Specific Driver. These drivers provide an abstraction layer for the underlying hardware by allowing the operating system to call generic functions to perform platform-specific operations without worrying about the actual hardware implementation. This allows OS/2 Warp Server for SMP to support new MP hardware platforms without modifying the operating system.

PSDs are 32-bit flat DLLs specified in CONFIG.SYS by using the PSD= keyword, and must conform to the 8.3 file naming convention (e.g. PSD= BELIZE.PSD). They cannot contain either drive or path information because OS/2 cannot process such information at the stage of the startup sequence when the PSD statements are processed. The root directory of the startup partition is first searched for the specified file name, followed by the \OS2 directory of the startup partition. If drive or path information is included in a PSD statement, an error is generated.

PSD parameters may be specified after the PSD's name, and may be a maximum of 1024 characters long. The parameter string is not interpreted or parsed by OS/2, but is passed verbatim as an ASCIIZ string when the PSD's Install function is invoked.

If multiple PSD statements are encountered, OS/2 will load each PSD in the order listed in CONFIG.SYS, and call the PSD's install function. The first PSD which successfully installs will be the one OS/2 uses.

PSD statements are processed before BASEDEV, IFS, and DEVICE statements.

---

Platform Specific Driver Architecture and Structure

The PSD operates in three contexts (modes): Kernel, Interrupt, and Init.

Kernel Mode

The OS/2 kernel calls the PSD for task-time operations, that is, it will execute as a thread within a process. Kernel mode is also referred to as the task context.

Interrupt Mode

The OS/2 kernel calls the PSD for interrupt-time operations. Interrupt time is a generic term that refers to executing code as a result of a hardware interrupt. The code does not execute as a thread belonging to a process.

Init Mode

The PSD is currently being used for system initialization. A limited set of PSD helps are available for use.

PSDs may contain multiple code and data objects. All objects will be fixed (not-swappable or movable) in low physical memory, with virtual addresses in the system arena. Objects are loaded in low physical memory to facilitate the use of real mode or bi-modal code. All objects default to permanent, which means that they remain in the system after initialization is completed. The SEGMENTS directive and the IOPL option in the linker DEF file should be used to mark those objects that are not to be kept after initialization.

The multitasking/multiprocessing environment of OS/2 Warp Server for SMP dictates that the PSD must be capable of handling multiple requests simultaneously. This means that global variables should be used sparingly. Upon PSD installation , the kernel passes a pointer to a small area of processor local memory (PLMA) which the PSD developer can use to store variables.  This PLMA area is currently 256 bytes in size.  IBM reserves the right to increase the size of this area in future releases of OS/2, though the minimum available will always be 256 bytes. The size of the PLMA is passed in the same structure as the pointer to the PLMA from the PSDINSTALL export.

PSD developers must be aware of the effects of the calls they make, because there is no guarantee that if an operation is started on a processor, and execution blocks, that the operation will continue on the same processor. OS/2 does not preempt a thread in the PSD, but it may block as a result of using a PSD help, or it may be interrupted by a hardware interrupt.

PSDs can register an interrupt handler for any given IRQ level using the SET_ IRQ PSD help. These interrupt handlers are guaranteed to be called before any device driver's interrupt handler. If the PSD's interrupt handler returns NO_ERROR, the interrupt manager will assume the interrupt has been handled, it will end the interrupt. If a -1 is returned, the interrupt manager will assume that the interrupt has not been handled, and will call each device driver which has a registered interrupt handler for that particular level until one claims the interrupt. If the interrupt is unclaimed, the IRQ level will be masked off.

All PSDs must use the SET_IRQ PSD help to indicate which IRQ level they will be using for inter-processor interrupts (IPI). If the PSD's IPI IRQ level is shared, it must register a handler which detects if the IRQ is an IPI or another interrupt. The handler must return NO_ERROR if the interrupt was caused by an IPI, otherwise it returns a -1. If the IPI IRQ level is unique, an interrupt handler need not be installed but SET_IRQ must still be used to indicate which is the IPI IRQ level.

The kernel will save the state of all the registers (except EAX) around calls to the PSD functions. All the functions will run at Ring 0. Upon invocation, SS, DS, and ES will be flat. The PSD functions must conform to the C calling convention. They receive parameters on the stack (4 bytes per parameter), and must return a return code in EAX.

The PSD functions have been split into three categories:

  • Functions that the PSD must have for OS/2 to operate (required functions)
  • Functions that the PSD does not need to have (optional functions)
  • Functions that the PSD must have for OS/2 to use multiple processors (MP

functions).

The kernel provides default handling for some of the PSD functions. PSD functions can also chain to a kernel default handler by returning a -1 return code. If a return code other than -1 is returned by a PSD function, the default handler will not get called. The PSD function glossary later in this chapter details the categories of all the functions, as well as any default handlers they may have. The PSD developer makes functions available to OS/2 by using the EXPORTS statement in the module definition (DEF) file. All functions should be exported using upper case with no leading underscores (_). An example is shown below.

  LIBRARY TESTPSD

  EXPORTS

     PSD_INSTALL   = _Install
     PSD_DEINSTALL = _DeInstall

The initial CS and EIP in the PSD's executable image is ignored. The image should also not contain a stack object. OS/2 allocates a per-processor PSD stack and sets SS and ESP correctly before invoking any of the PSD functions. PSDs should be written in flat 32-bit code, using C, and must be linked as a LIBRARY. OS/2 invokes all PSD functions in protect mode, but there is also a PSD help which allows the PSD developer to call a PSD function in real mode. OS/2 services are provided through the PSD help interface. Access to these services are obtained upon PSD installation. PSD helpers preserve all registers except EAX. All the definitions (e.g. defines, structures, etc.) that are required for building a PSD are in the header file PSD.H.

---

OS/2 Initialization

OS/2 requires a PSD for system initialization. The system will display an error message if a valid PSD for the current platform cannot be installed. The following is a list of steps, in the order in which they occur, that are executed after a PSD is installed. If any step does not complete successfully, the system initialization process will stop, and a fatal error message will be displayed .

  1. After a PSD is successfully installed, its Init function is invoked

. This function is used to allocate and initialize any resources that the PSD may require, as well as initializing the state of the hardware.

  1. The kernel determines the number of usable processors on the current

platform by using the PSD_GET_NUM_OF_PROCS function.

  1. The kernel allocates all resources required to support the additional

processors. This step determines what to allocate based on the results of the previous step.

  1. The PSD's processor initialization function is invoked on the

current processor (CPU0).

  1. An MP daemon is created for CPU0. An MP daemon is a thread that

never goes away, which is used for MP operations by a specific processor.

  1. An MP daemon is created for the next logical processor.
  2. The PSD's start processor call is invoked to start the next

logical processor. The PSD should only start the specified processor, and then return (see the PSD_START_PROC function for more detail). The started processor will spin in a tight loop waiting for a variable to be cleared. This variable is referred to as the processor initialization real mode spinlock.

  1. Upon return from the PSD's start processor call, the processor

initialization real mode spinlock is cleared.

  1. CPU0 will spin in a tight loop waiting for a variable to be cleared

. This variable is referred to as the CPU0 spinlock.

  1. The started processor continues execution of the kernel's real

mode processor initialization code now that processor's initialization real mode spinlock has been cleared.

  1. The started processor sets up all protect mode and paging information,

and switches into protect mode with paging enabled.

  1. Up to this point, the started processor has been running on a small

processor initialization stack (It has not been running as an OS/2 thread). The current context is switched to that of this processors MP daemon.

  1. OS/2 calls the PSD's processor initialization function for the

current processor.

  1. The PSD indicates that the processor has been initialized.
  2. The started processor will spin in a tight loop waiting for a variable

to be cleared. This variable is referred to as the processor initialization protect mode spinlock.

  1. The CPU0 spinlock is cleared.
  2. System initialization continues on CPU0 now that its spinlock has been

cleared.

  1. Steps 6, through 17 are repeated until all processors have been

started.

  1. The rest of system initialization continues normally, on CPU0.
  2. After the system is fully initialized, the processor initialization

protect mode spinlock is cleared. This allows CPU1 through CPU-N to start executing code.

---

PSD Function Glossary

In the functions listed below all pointers must be flat 32-bit linear addresses. The following keywords indicate:

Required

Indicates that the function is required for OS/2 to operate properly , so the function can not be omitted.

Optional

Indicates that the function is not required.

MP

Indicates that the function is not required for OS/2 to execute with one processor, but it is required for OS/2 to use multiple processors.

Default

Indicates that the OS/2 kernel provides default handling for that specific function.

Can Block

Indicates that the function can call a PSD help that may block.

Can't Block

Indicates that the function can not call a PSD help that may block.

Input

Indicates that the kernel fills the field with input values before calling the function.

Output

Indicates that the PSD should return values in the specified field .

0-based

Indicates that a zero denotes the first value.

1-based

Indicates that a one denotes the first value.

---

PSD Functions

Following are the PSD functions.

PSD Helps

OS/2 provides system services to the PSD developer via PSD helps. The address of the PSD help router is passed in the INSTALL structure when a PSD's install function is called.  All PSD helps destroy the contents of the EAX register (used for a return code).  All other registers, including the flags, are preserved. To invoke a PSD help, set up the appropriate parameters and call the PSD help router.  For an example, refer to Sample Source Code for a PSD. Some prototypes and macros are defined in PSD.H to simplify their usage. The following keywords indicate:

May Block

Indicates that the help may block.

Won't Block

Indicates that the help won't block.

Application Programming Interface

The OS/2 kernel will provide new support APIs for PSDs.