Document revision date: 30 March 2001

## OpenVMS Programming Concepts Manual

Previous | Contents | Index

1. The application opens DECDTM$EXAMPLE1.FILE1 and DECDTM$EXAMPLE1.FILE2 for writing. It then zeroes the variable COUNT and enters an infinite loop.
2. The application increments the count by one and converts it to an ASCII string.
3. The application calls SYS$START_TRANSW to start a transaction. The application checks the immediate return status and service completion status to see whether they signify an error.
4. The application attempts to write the string to the two files. If it cannot, the application aborts the transaction. Because the files are OpenVMS RMS journaled files, the default transaction is assumed.
5. The application calls SYS$END_TRANSW to attempt to commit the transaction. It checks the immediate return status and service completion status to see whether they signify an error. If they do, the application reports the error and exits. If there are no errors, the transaction is committed and the application continues with the loop.
6. If either of the two files could not be updated, the application calls SYS$ABORT_TRANSW to abort the transaction. It checks the immediate return status and service completion status to see whether they signify an error. If they do, the application reports the error and exits.

## Chapter 30
Creating User-Written System Services

This chapter describes how to create user-written system services. It contains the following sections:

Section 30.1 describes privileged routines and privileged shareable images.

Section 30.2 describes how to write a privileged routine.

Section 30.3 describes how to create a privileged shareable image on VAX systems.

Section 30.4 describes how to create a privileged shareable image on Alpha systems.

## 30.1 Overview

Your application may contain certain routines that perform privileged functions, called user-written system services. To create these routines, put them in a privileged shareable image. User-mode routines in other modules can call the routines in the privileged shareable image to perform functions in a more privileged mode.

You create a privileged shareable image as you would any other shareable image, using the /SHAREABLE qualifier with the linker. (For more information about how to create a shareable image, see the OpenVMS Linker Utility Manual.) However, because a call to a routine in a more privileged mode must be vectored through the system service dispatch routine, you must perform some additional steps. The following steps outline the basic procedure. Section 30.3 provides more detail about requirements specific to VAX systems. Section 30.4 describes the necessary steps for Alpha systems.

1. Create the source file. The source file for a privileged shareable image contains the routines that perform privileged functions. In addition, because user-written system services are called using the system service dispatcher, you must include a privileged library vector (PLV) in your shareable image. A PLV is an operating-system-defined data structure that communicates the location of the privileged routines to the operating system.
   On VAX systems, the PLV contains the addresses of dispatch routines for each access mode used in the image. You must write these dispatch routines and include them in your shareable image. Section 30.3.1 provides more information.
   On Alpha systems, you list the names of the privileged routines in the PLV, sorted by access mode. You do not need to create dispatch routines; the image activator creates them for you automatically.
   Section 30.2 provides guidelines for creating privileged routines.
2. Compile or assemble the source file.
3. Create the shareable image. You create a privileged shareable image as you would any other shareable image: by specifying the /SHAREABLE qualifier to the LINK command. Note, however, that creating privileged shareable images has some additional requirements. The following list summarizes these requirements. See the OpenVMS Linker Utility Manual for additional information about linker qualifiers and options.
   - Declare the privileged routine entry points as universal symbols. Privileged shareable images use the same mechanisms to declare universal symbols as other shareable images: transfer vectors on VAX and symbol vectors on Alpha systems. However, because calls to user-written system services must be vectored through the system service dispatcher, you must use extensions to these mechanisms for privileged shareable images. Section 30.3.3 describes how to declare a universal symbol in a VAX privileged shareable image. Section 30.4.2 describes how to declare a universal symbol in an Alpha system privileged shareable image.
   - Prevent the linker from processing the system default shareable image library, SYS$LIBRARY:IMAGELIB.OLB, by specifying the /NOSYSSHR linker qualifier. Otherwise, the linker processes this library by default.
   - Protect the shareable image from user-mode access by specifying the /PROTECT linker qualifier. If you want to protect only certain portions of the shareable image, instead of the entire image, use the PROTECT= linker option.
   - Set the VEC attribute of the program section containing the PLV by using the PSECT_ATTR= linker option. Modules written in MACRO can specify this attribute in the .PSECT directive. The PLV must appear in a program section with the VEC attribute set.
   - Set the shareable image identification numbers using the GSMATCH= option.
   If your privileged application requires that you link against the system executive, see the OpenVMS Linker Utility Manual for more information.
4. Install the privileged shareable image as a protected permanent global section. Privileged shareable images must be installed to be available to nonprivileged programs. The following procedure is recommended:
   1. Move the privileged shareable image to a protected directory, such as SYS$SHARE.
   2. Invoke the Install utility, specifying the /PROTECT, /OPEN, and /SHARED qualifiers. You can also specify the /HEADER_RESIDENT qualifier. The following entry could be used to install a user-written system service whose image name is MY_PRIV_SHARE:

          $ INSTALL
          INSTALL> ADD SYS$SHARE:MY_PRIV_SHARE/PROTECT/OPEN/SHARED/HEADER_RES

To use a privileged shareable image, you include it in a link operation as you would any other shareable image: specifying the shareable image in a linker options file with the /SHAREABLE qualifier appended to the file specification to identify it as a shareable image.

## 30.2 Writing a Privileged Routine (User-Written System Service)

On both VAX systems and Alpha systems, the routines that implement user-written system services must enable any privileges they need that the nonprivileged user of the user-written system service lacks. The user-written system service must also disable any such privileges before the nonprivileged user receives control again. To enable or disable a set of privileges, use the Set Privileges ($SETPRV) system service. The following example shows the operator (OPER) and physical I/O (PHY_IO) privileges being enabled. (Any code executing in executive or kernel mode is granted an implicit SETPRV privilege so it can enable any privileges it needs.)

    PRVMSK:  .LONG   <1@PRV$V_OPER>!<1@PRV$V_PHY_IO> ;OPER and PHY_IO
             .LONG   0     ;quadword mask required.  No bits set in
                           ;high-order longword for these privileges.
               .
               .
               .
            $SETPRV_S  ENBFLG=#1,-       ;1=enable, 0=disable
                       PRVADR=PRVMSK     ;Identifies the privileges

When you design your system service, you must carefully define the boundaries between the protected subsystem and the user who calls the service. A protected image has privileges to perform tasks on its own behalf. When your image performs tasks on behalf of users, you must ensure that your image performs only those tasks the users could not have done on their own. Always keep the following coding principles in mind:

- Keep privileges off, and turn them on only when necessary.
- Make sure privileges are off on all exit paths. When you perform a task for the user, operate in user mode whenever possible and operate at all times with the user's privileges, identity, and so on. Make sure that operating in an inner mode does not give you any special privileges with respect to the operation being performed. Resume a privileged state only when you are about to resume operation on your own behalf.
- If user input can affect an operation executed with privilege, you have to carefully validate the input. Never pass user parameters directly to an operation executed in an inner mode or with privilege. When designing your program, keep in mind that the inner modes implicitly provide a user with the system privileges SETPRV, CMKRNL, SYSNAM, and SYSLCK. (See the OpenVMS Guide to System Security for descriptions.)
- As a protected image, your program does not have the entire operating system programming environment at its disposal. Unless a module has the prefix SYS$ or EXE$, you must avoid calling it from an inner mode. In particular, do not call LIB$GET_VM or LIB$RET_VM from an inner mode. You can call OpenVMS RMS routines from executive mode but not from kernel mode.
  On VAX systems, Version 5.4 or later of the operating system, any OpenVMS RMS files that were opened with privilege from an inner mode can be left open during user execution; however, this is not acceptable on earlier versions of the operating system.
- Never make subroutine calls to other shareable images from kernel or executive mode.
- When a protected subsystem opens a file on its own behalf, it should specify executive-mode logical names only by naming executive mode explicitly in the FAB$V_LNM_MODE subfield of the file access block (FAB). This prevents a user's logical name from redirecting a file specification.

On VAX systems, refer to SYS$EXAMPLES:USSDISP.MAR and USSTEST.MAR for listings of modules in a user-written system service and of a module that calls the user-written system service.

On Alpha systems, for C examples refer to SYS$EXAMPLES:UWSS.C and SYS$EXAMPLES:UWSS_TEST.C.

## 30.3 Creating a Privileged Shareable Image (VAX Only)

On VAX systems, you must create dispatch routines that transfer control to the privileged routines in your shareable image. You then put the addresses of these dispatch routines in a privileged library vector (PLV). Section 30.3.1 describes how to create a dispatch routine. Section 30.3.2 describes how to create a PLV.

### 30.3.1 Creating User-Written Dispatch Routines on VAX Systems

On VAX systems, you must create kernel-mode and executive-mode dispatching routines that transfer control to the routine entry points. You must supply one dispatch routine for all your kernel mode routines and a separate routine for all the executive mode routines. The dispatcher is usually written using the CASE construct, with each routine identified by a code number. Make sure that the identification code you use in the dispatch routine and the code specified in the transfer vector identify the same routine.

The image activator, when it activates a privileged shareable image, obtains the addresses of the dispatch routines from the PLV and stores these addresses at a location known to the system service dispatcher. When a call to a privileged routine is initiated by a CHME or CHMK instruction, the system service dispatcher attempts to match the code number with a system service code. If there is no match, it transfers control to the location where the image activator has stored the address of your dispatch routines.

A dispatch routine must validate the CHMK or CHME operand identification code number, handling any invalid operands. In addition, the dispatching routine must transfer control to the appropriate routine for each identification code if the user-written system service contains functionally separate coding segments. The CASE instruction in VAX MACRO or a computed GOTO-type statement in a high-level language provides a convenient mechanism for determining where to transfer control.

Note Users of your privileged shareable image must specify the same code number to identify a privileged routine as you used to identify it in the dispatch routine. Users specify the code number in their CHMK or CHME instruction. See Section 30.3.3 for information about transfer vectors.

In your source file, a dispatch routine must precede the routines that implement the user-written system service.

Example 30-1 illustrates a sample dispatching routine, taken from the sample privileged shareable image in SYS$EXAMPLES named USSDISP.MAR.

Example 30-1 Sample Dispatching Routine

    KERNEL_DISPATCH::                       ; Entry to dispatcher
            MOVAB   W^-KCODE_BASE(R0),R1    ; Normalize dispatch code value
            BLSS    KNOTME                  ; Branch if code value too low
            CMPW    R1,#KERNEL_COUNTER      ; Check high limit
            BGEQU   KNOTME                  ; Branch if out of range
    ;
    ; The dispatch code has now been verified as being handled by this dispatcher,
    ; now the argument list will be probed and the required number of arguments
    ; verified.
    ;
            MOVZBL  W^KERNEL_NARG[R1],R1    ; Get required argument count
            MOVAL   @#4[R1],R1              ; Compute byte count including argcount
            IFNORD  R1,(AP),KACCVIO         ; Branch if arglist not readable
            CMPB    (AP),W^<KERNEL_NARG-KCODE_BASE>[R0] ; Check for required number
            BLSSU   KINSFARG                ;  of arguments
            MOVL    FP,SP                   ; Reset stack for service routine
            CASEW   R0,-                    ; Case on change mode
            .
            .
            .

### 30.3.2 Creating a PLV on VAX Systems

On VAX systems, a call to a privileged routine goes to the transfer vector that executes a change mode instruction (CHMx) specifying the identification code of the privileged routine as the operand to the instruction. The operating system routes the change mode instruction to the system service dispatch routine, which attempts to locate the system service with the code specified. Because the code is a negative number, the system service dispatcher drops through its list of known services and transfers control to a user-written dispatch routine, if any have been specified.

The image activator has already placed at this location the address of whatever user-written dispatch routines it found in the privileged shareable image's PLV when it activated the PLV. The dispatch routine transfers control to the routine in the shareable image identified by the code. (You must ensure that the code used in the transfer vector and the code specified in the dispatch routine both identify the same routine.) Figure 30-1 illustrates this flow of control.

Figure 30-1 Flow of Control Accessing a Privileged Routine on VAX Systems

Figure 30-2 shows the components of the PLV in VAX shareable images.

Figure 30-2 Components of the Privileged Library Vector on VAX Systems

Table 30-1 describes each field in the PLV on a VAX processor, including the symbolic names the operating system defines to access each field. These names are defined by the $PLVDEF macro in SYS$LIBRARY:STARLET.MLB.

Table 30-1 Components of the VAX Privileged Library Vector
Component | Symbol | Description
--- | --- | ---
Vector type code | PLV$L_TYPE | Identifies the type of vector. For PLVs, you must specify the symbolic constant defined by the operating system, PLV$C_TYP_CMOD, which identifies a privileged library vector.
Kernel-mode dispatcher | PLV$L_KERNEL | Contains the address of the user-supplied kernel-mode dispatching routine if your privileged library contains routines that run in kernel mode. The address is expressed as an offset relative to the start of the data structure (self-relative pointer). A value of 0 indicates that a kernel-mode dispatcher does not exist.
Executive-mode dispatcher | PLV$L_EXEC | Contains the address of the user-supplied executive-mode dispatching routine if your privileged library contains routines that run in executive mode. The address is expressed as an offset relative to the start of the data structure (self-relative pointer). A value of 0 indicates that a kernel-mode dispatcher does not exist.
User-supplied rundown routine | PLV$L_USRUNDWN | Contains the address of a user-supplied rundown routine that performs image-specific cleanup and resource deallocation if your privileged library contains such a routine. When the image linked against the user-written system service is run down by the system, this run-time routine is invoked. Unlike exit handlers, the routine is always called when a process or image exits. (The image rundown code calls this routine with a JSB instruction; it returns with an RSB instruction called in kernel mode at IPL 0.)
RMS dispatcher | PLV$L_RMS | Contains the address of a user-supplied dispatcher for OpenVMS RMS services. A value of 0 indicates that a user-supplied OpenVMS RMS dispatcher does not exist. Only one user-written system service should specify the OpenVMS RMS vector, because only the last value is used. This field is intended for use only by Compaq.
Address check | PLV$L_CHECK | Contains a value to verify that a user-written system service that is not position independent is located at the proper virtual address. If the image is position independent, this field should contain a zero. If the image is not position independent, this field should contain its own address.

Example 30-2 illustrates how the sample privileged shareable image in SYS$EXAMPLES assigns values to the PLV.

Example 30-2 Assigning Values to a PLV on a VAX System

            .PAGE
            $PLVDEF                         ; Define PLV fields
            .SBTTL  Change Mode Dispatcher Vector Block
    (1)      .PSECT  USER_SERVICES,PAGE,VEC,PIC,NOWRT,EXE

    (2)    .LONG   PLV$C_TYP_CMOD          ; Set type of vector to change mode
            .LONG   0                       ; Reserved
            .LONG   KERNEL_DISPATCH-.       ; Offset to kernel mode dispatcher
            .LONG   EXEC_DISPATCH-.         ; Offset to executive mode dispatcher
            .LONG   USER_RUNDOWN-.          ; Offset to user rundown service
            .LONG   0                       ; Reserved.
            .LONG   0                       ; No RMS dispatcher
            .LONG   0                       ; Address check - PIC image

1. The sample program sets the VEC attribute of the program section containing the PLV.
2. Values are assigned to each field of the PLV.

### 30.3.3 Declaring Privileged Routines as Universal Symbols Using Transfer Vectors on VAX Systems

On VAX systems, you use the transfer vector mechanism to declare universal symbols (described in the OpenVMS Linker Utility Manual). However, for privileged shareable images, the transfer vector must also contain a CHMx instruction because the target routine operates in a more privileged mode. You identify the privileged routine by its identification code, supplied as the only operand to the CHMx instruction. Note that the code number used must match the code used to identify the routine in the dispatch routine. The following example illustrates a typical transfer vector for a privileged routine:

    .TRANSFER  my_serv
    .MASK      my_serv
    CHMK  <code_number>
    RET

Because the OpenVMS system services codes are all positive numbers and because the call to a privileged routine is initially handled by the system service dispatcher, you should assign negative code numbers to identify your privileged routines so they do not conflict with system services identification codes.

## 30.4 Creating a User-Written System Service (Alpha Only)

On Alpha systems, in addition to the routines that perform privileged functions, you must also include a PLV in your source file. However, on Alpha systems, you list the privileged routines by name in the PLV. You do not need to create a dispatch routine that transfers control to the routine; the routine is identified by a special code.

### 30.4.1 Creating a PLV on Alpha Systems

On Alpha systems, the PLV contains a list of the actual addresses of the privileged routines. The image activator creates the dispatch routines. Figure 30-3 illustrates the linkage for a privileged routine on Alpha systems.

Figure 30-3 Linkage for a Privileged Routine After Image Activation

Table 30-2 describes the components of the privileged library vector on Alpha systems.

Table 30-2 Components of the Alpha Privileged Library Vector
Component | Symbol | Description
--- | --- | ---
Vector type code | PLV$L_TYPE | Identifies the type of vector. You must specify the symbolic constant, PLV$C_TYP_CMOD, to identify a privileged library vector.
System version number | PLV$L_VERSION | Specifies the system version number (unused).
Kernel-mode routine count | PLV$L_KERNEL_ROUTINE_COUNT | Specifies the number of user-supplied kernel-mode routines listed in the kernel-mode routine list. The address of this list is specified in PLV$PS_KERNEL_ROUTINE_LIST.
Executive-mode routine count | PLV$L_EXEC_ROUTINE_COUNT | Specifies the number of user-supplied executive-mode routines listed in the executive-mode routine list. The address of this list is specified in PLV$PS_EXEC_ROUTINE_LIST.
Kernel-mode routine list | PLV$PS_KERNEL_ROUTINE_LIST | Specifies the address of a list of user-supplied kernel-mode routines.
Executive-mode routine list | PLV$PS_EXEC_ROUTINE_LIST | Specifies the address of a list of user-supplied executive-mode routines.
User-supplied rundown routine | PLV$PS_KERNEL_RUNDOWN_HANDLER | May contain the address of a user-supplied rundown routine that performs image-specific cleanup and resource deallocation. When the image linked against the user-written system service is run down by the system, this run-time routine is invoked. Unlike exit handlers, the routine is always called when a process or image exits. (Image rundown code calls this routine with a JSB instruction; it returns with an RSB instruction called in kernel mode at IPL 0.)
Thread-safe system service | PLV$M_THREAD_SAFE | Flags the system service dispatcher that the service requires no explicit synchronization. It is assumed by the dispatcher that the service provides its own internal data synchronization and that multiple kernel threads can safely execute the service in parallel.
RMS dispatcher | PLV$PS_RMS_DISPATCHER | Specifies the address of an alternative RMS dispatching routine.
Kernel Routine Flags Vector | PLV$PS_KERNEL_ROUTINE_FLAGS | Contains either the address of an array of quadwords that contains the defined flags associated with each kernel system service, or a zero. If a flag is set, the kernel mode service may return the status SS$_WAIT_CALLERS_MODE.
Executive Routine Flags Vector | PLV$PS_EXEC_ROUTINE_FLAGS | Contains a zero value, because there are no defined flags for executive mode.

Example 30-3 illustrates how to create a PLV on Alpha systems.

Example 30-3 Creating a PLV on Alpha Systems

    ! What follows is the definition of the PLV. The PLV lives
    ! in its own PSECT, which must have the VEC attribute. The
    ! VEC attribute is forced in the linker. The PLV looks like
    ! this:
    !
    !   +-------------------------------------+
    !   |         Vector Type Code            | PLV$L_TYPE
    !   |         (PLV$C_TYP_CMOD)            |
    !   +-------------------------------------+
    !   |       System Version Number         | PLV$L_VERSION
    !   |             (unused)                |
    !   +-------------------------------------+
    !   |     Count of Kernel Mode Services   | PLV$L_KERNEL_ROUTINE_COUNT
    !   |                                     |
    !   +-------------------------------------+
    !   |     Count of Exec Mode Services     | PLV$L_EXEC_ROUTINE_COUNT
    !   |                                     |
    !   +-------------------------------------+
    !   |  Address of a List of Entry Points  | PLV$PS_KERNEL_ROUTINE_LIST
    !   |       for Kernel Mode Services      |
    !   +-------------------------------------+
    !   |  Address of a List of Entry Points  | PLV$PS_EXEC_ROUTINE_LIST
    !   |         for Exec Mode Services      |
    !   +-------------------------------------+
    !   |        Address of Kernel Mode       | PLV$PS_KERNEL_RUNDOWN_HANDLER
    !   |             Rundown Routine         |
    !   +-------------------------------------+
    !   |                                     | PLV$M_THREAD_SAFE
    !   |                                     |
    !   +-------------------------------------+
    !   |      Address of Alternative RMS     | PLV$PS_RMS_DISPATCHER
    !   |          Dispatching Routine        |
    !   +-------------------------------------+
    !   |      Kernel Routine Flags Vector    | PLV$PS_KERNEL_ROUTINE_FLAGS
    !   |                                     |
    !   +-------------------------------------+
    !   |      Exec Routine Flags Vector      | PLV$PS_EXEC_ROUTINE_FLAGS
    !   |                                     |
    !   +-------------------------------------+
    !
    PSECT OWN = USER_SERVICES (NOWRITE, NOEXECUTE);

    OWN PLV_STRUCT : $BBLOCK[PLV$C_LENGTH] INITIAL (LONG (PLV$C_TYP_CMOD,! Type
                                                        ! of vector
    0,                                                  ! System version number
    (KERNEL_TABLE_END - KERNEL_TABLE_START) / %UPVAL,   ! Number of kernel mode
                                                        ! services
    (EXEC_TABLE_END - EXEC_TABLE_START) / %UPVAL,       ! Number of exec mode
                                                        ! services
    KERNEL_TABLE_START,   ! Address of list of kernel mode service routine
    EXEC_TABLE_START,     ! Address of list of exec mode service routine
    RUNDOWN_HANDLER,      ! Address of list of kernel mode rundown routine
    0,                    ! Reserved longword
    0,                    ! Address of alternate RMS dispatcher
    0,                    ! reserved
    0));                  ! reserved

    PSECT OWN = $OWN$;

Previous | Next | Contents | Index

| privacy and legal statement |
5841PRO_078.HTML